[ https://issues.apache.org/jira/browse/LANG-1323?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15979111#comment-15979111 ]
Scott Kilpatrick edited comment on LANG-1323 at 4/21/17 6:40 PM: ----------------------------------------------------------------- Here's an example in JUnit: {code:java} static class OneField { Map<String, Integer> f; } @Test public void test() throws NoSuchFieldException { final Type openJdkType = OneField.class.getDeclaredField("f").getGenericType(); final Type apacheType = TypeUtils.parameterize(Map.class, String.class, Integer.class); Assert.assertTrue(openJdkType.equals(apacheType)); Assert.assertTrue(apacheType.equals(openJdkType)); Assert.assertFalse(openJdkType.hashCode() == apacheType.hashCode()); // Example with Guava's ImmutableSet, which for N > 1 uses // hash code for set equality, and Iterables.elementsEqual. final Type other = OneField.class; final ImmutableSet<Type> c1 = ImmutableSet.of(openJdkType, other); final ImmutableSet<Type> c2 = ImmutableSet.of(apacheType, other); Assert.assertFalse(c1.equals(c2)); Assert.assertFalse(c2.equals(c1)); Assert.assertTrue(c1.size() == c2.size()); Assert.assertTrue(Iterables.elementsEqual(c1, c2)); } {code} Is this not a violation of the contract on {{Object.hashCode()}} that I quoted above? Here are two objects that are equal according to the {{equals(Object)}} method, but calling the {{hashCode}} method on each of the two objects produces different integer results. was (Author: skilpat): Here's an example in JUnit: {code:java} static class OneField { Map<String, Integer> f; } @Test public void test() throws NoSuchFieldException { final Type openJdkType = OneField.class.getDeclaredField("f").getGenericType(); final Type apacheType = TypeUtils.parameterize(Map.class, String.class, Integer.class); Assert.assertTrue(openJdkType.equals(apacheType)); Assert.assertTrue(apacheType.equals(openJdkType)); Assert.assertFalse(openJdkType.hashCode() == apacheType.hashCode()); // Example with Guava's ImmutableSet and Iterables, which for N > 1 uses // hash code for set equality. final Type other = OneField.class; final ImmutableSet<Type> c1 = ImmutableSet.of(openJdkType, other); final ImmutableSet<Type> c2 = ImmutableSet.of(apacheType, other); Assert.assertFalse(c1.equals(c2)); Assert.assertFalse(c2.equals(c1)); Assert.assertTrue(c1.size() == c2.size()); Assert.assertTrue(Iterables.elementsEqual(c1, c2)); } {code} Is this not a violation of the contract on {{Object.hashCode()}} that I quoted above? Here are two objects that are equal according to the {{equals(Object)}} method, but calling the {{hashCode}} method on each of the two objects produces different integer results. > Type implementations in TypeUtils compute hash code that breaks > Object.equals() with Sun's OpenJDK > -------------------------------------------------------------------------------------------------- > > Key: LANG-1323 > URL: https://issues.apache.org/jira/browse/LANG-1323 > Project: Commons Lang > Issue Type: Bug > Components: lang.reflect.* > Affects Versions: 3.2, 3.5 > Environment: Sun OpenJDK > Reporter: Scott Kilpatrick > Priority: Minor > > {{TypeUtils}} in {{lang.reflect}} provides convenient methods for creating > objects of the interface {{Type}}. Those objects are defined by the following > classes: > * ParameterizedTypeImpl (implements {{ParameterizedType}}) > * WildcardTypeImpl (implements {{WildcardType}}) > * GenericArrayTypeImpl (implements {{GenericArrayType}}) > Similarly, there are corresponding classes, which implement the same > interfaces, defined in one's particular JDK. And it's these latter classes > that are instantiated when you get objects of type {{Type}} via reflection. > Let's call these the "internal {{Type}} implementations." In the case of > Sun's OpenJDK, [they are > defined|http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/reflect/generics/reflectiveObjects] > in package {{sun.reflect.generics.reflectiveObjects}}. > Each of the {{TypeUtils}} classes implements {{Object.equals(Object)}} in a > general way that's compatible with the internal {{Type}} implementations. For > example, if I access a field declared with type {{Map<String, Integer>}} and > get its generic type, via {{Field.getGenericType()}}, then that will be equal > to the {{TypeUtils}} object returned by: > {code:java} > TypeUtils.parameterize(Map.class, String.class, Integer.class) > {code} > That's what I'd expect, so that's great. > However, the {{TypeUtils}} classes implement their {{Object.hashCode()}} > method in a _different_ way from the corresponding implementations in Sun > OpenJDK implementations. That's not so surprising, _but it breaks the > contract of {{Object.hashCode()}}_: > bq. 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. > In other words, the two {{Type}} objects above will both consider themselves > {{equals}} to each other, but they have different hash codes. > One example of a negative consequence of this problem is a collection class > that implements its equality (to other collections) by checking hash codes of > its elements, e.g., Guava's immutable collections. If you have {{Type}} > objects in those collections, with {{TypeUtils}} {{Type}} objects in {{c1}} > and Sun OpenJDK {{Type}} objects in {{c2}}, you will see that > {{c1.equals(c2)}} returns {{false}} -- because their elements don't all have > the same hash codes -- even though those elements are all considered equal. -- This message was sent by Atlassian JIRA (v6.3.15#6346)