SYNOPSIS
--------
hashCode() implementation for java.awt.SystemColor / java.awt.Color is flawed
OPERATING SYSTEM
----------------
All
FULL JDK VERSION
----------------
All Java 6 releases are affected by the Java 6 issue
All Java 7 builds are affected by the Java 7 issue
PROBLEM DESCRIPTION from LICENSEE
---------------------------------
The results of calling hashCode() on the static SystemColor instances defined in the SystemColor class break the hashCode() contract on Java 6, and Java 7, for different reasons on each release.
In the API documentation for java.lang.Object, the general contract for hashCode() states:
* Whenever it is invoked on the same object more than once during an
execution of a Java application, the hashCode method must consistently
return the same integer, provided no information used in equals
comparisons on the object is modified. This integer need not remain
consistent from one execution of an application to another execution
of the same application.
* If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce
the same integer result.
On Java 6, SystemColor.hashCode() breaks the second part of the contract. Two SystemColor instances can be equal even though their hash codes are different.
That problem is fixed on Java 7, where the integer returned by SystemColor.hashCode() is identical to the integer returned by SystemColor.getRGB(). Both calls actually invoke the methods in the parent Color class, unlike Java 6 where hashCode() is overridden in the SystemColor class. However, this causes another problem. Consider the following statement in the SystemColor API documentation:
For systems which support the dynamic update of the system colors
(when the user changes the colors) the actual RGB values of these
symbolic colors will also change dynamically.
Since hashCode() is returning the RGB colour value, and that RGB colour value can change dynamically, it follows that the return value of hashCode() can also change during the execution of a Java application, thus violating part 1 of the hashCode() contract. This is proved by the second testcase provided below.
REPRODUCTION INSTRUCTIONS - JAVA 6 ISSUE
----------------------------------------
1. Compile and run SystemColorTest1.java (attached)
2. Observe the following behaviour on Java 6:
Found equals() / hashCode() contract violation:
Compared java.awt.SystemColor[i=14] to java.awt.SystemColor[i=1]
java.awt.SystemColor[i=14] hashcode = 14
java.awt.SystemColor[i=1] hashcode = 1
java.awt.SystemColor[i=14] RGB value = -13410648
java.awt.SystemColor[i=1] RGB value = -13410648
Test failed!
3. Observe the following result on Java 7, which represents the expected
behaviour:
Test passed!
REPRODUCTION INSTRUCTIONS - JAVA 7 ISSUE
----------------------------------------
1. Compile and run SystemColorTest2.java (attached)
2. While the testcase is running, change the colour of the OS desktop
(for example, on Windows, simply right click the desktop and change
the setting in Properties->Desktop->Color)
3. Observe a result similar to the following with Java 7 (the integers
involved depend on the colours you change to/from):
hashCode() return value changed from -16777216 to -65536
Test Failed!
4. Observer the following result with Java 6 (the expected behaviour):
Test Passed!
TESTCASE SOURCE (ATTACHED)
------------------------------
SystemColorTest1 - JDK 6 Issue.
SystemColorTest2 - JDK 7 Issue.