-
Notifications
You must be signed in to change notification settings - Fork 18
Calling hashCode on two equal TypeTokens returns different results #17
Description
According to the javadocs for java.lang.Object#hashCode,
If two objects are equal according to the equals method, then calling the hashCode method on each of the two objects must produce the same integer result.
Currently, TypeToken does not abide by this.
It is possible to get two type tokens where first.equals(second) is true, but first.hashCode() == second.hashCode() is false.
This violates the contract of hashCode and breaks any attempt to use TypeToken as the key to a HashMap.
Here is some example code that can reproduce the issue:
import io.leangen.geantyref.TypeToken;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
TypeToken<Set<? extends Test>> testTypeToken = new TypeToken<>() {};
Method method = Test.class.getMethod("testFunction", Set.class);
TypeToken<?> otherTypeToken = TypeToken.get(method.getParameters()[0].getParameterizedType());
Map<TypeToken<?>, String> testMap = new HashMap<>();
testMap.put(otherTypeToken, "test");
System.out.println("testTypeToken: " + testTypeToken);
System.out.println("testTypeToken.getType(): " + testTypeToken.getType());
System.out.println("testTypeToken.hashCode(): " + testTypeToken.hashCode());
System.out.println();
System.out.println("otherTypeToken: " + otherTypeToken);
System.out.println("otherTypeToken.getType(): " + otherTypeToken.getType());
System.out.println("otherTypeToken.hashCode(): " + otherTypeToken.hashCode());
System.out.println();
System.out.println("testTypeToken.equals(otherTypeToken): " + testTypeToken.equals(otherTypeToken));
System.out.println("otherTypeToken.equals(testTypeToken): " + otherTypeToken.equals(testTypeToken));
System.out.println();
System.out.println("testMap: " + testMap);
System.out.println("testMap.get(testTypeToken): " + testMap.get(testTypeToken));
}
public static void testFunction(Set<? extends Test> testSet) {}
}The current output of this program is:
testTypeToken: Test$1@60f4877b
testTypeToken.getType(): java.util.Set<? extends Test>
testTypeToken.hashCode(): 1626638203
otherTypeToken: io.leangen.geantyref.TypeToken$2@17a57871
otherTypeToken.getType(): java.util.Set<? extends Test>
otherTypeToken.hashCode(): 396720241
testTypeToken.equals(otherTypeToken): true
otherTypeToken.equals(testTypeToken): true
testMap: {io.leangen.geantyref.TypeToken$2@17a57871=test}
testMap.get(testTypeToken): null
The expected output is:
testTypeToken: Test$1@60f4877b
testTypeToken.getType(): java.util.Set<? extends Test>
-testTypeToken.hashCode(): 1626638203
+testTypeToken.hashCode(): 396720241
otherTypeToken: io.leangen.geantyref.TypeToken$2@17a57871
otherTypeToken.getType(): java.util.Set<? extends Test>
otherTypeToken.hashCode(): 396720241
testTypeToken.equals(otherTypeToken): true
otherTypeToken.equals(testTypeToken): true
testMap: {io.leangen.geantyref.TypeToken$2@17a57871=test}
-testMap.get(testTypeToken): null
+testMap.get(testTypeToken): "test"As you can see, if reflection is used to get the parameterized type of a method, and that type is then added to a hash map, you cannot access that parameterized type using one constructed via new TypeToken<Set<? extends Test>>() {}.