commons-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Scott Kilpatrick (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (LANG-1323) Type implementations in TypeUtils compute hash code that breaks Object.equals() with Sun's OpenJDK
Date Fri, 21 Apr 2017 22:05:04 GMT

    [ https://issues.apache.org/jira/browse/LANG-1323?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15979471#comment-15979471
] 

Scott Kilpatrick commented on LANG-1323:
----------------------------------------

The Guava stuff was just an _additional example_ of a sneaky manifestation of the bug; it's
not crucial to the bug. Sorry to have included more than necessary in that example.

For posterity, here's the example demonstrating the broken contract, without the additional
broken use case:

{code:java}
    static class OneField {
        Map<String, Integer> f;
    }

    @Test
    public void testOfBrokenContract() throws NoSuchFieldException
    {
        // An object of class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
        final Type openJdkType = OneField.class.getDeclaredField("f").getGenericType();

        // An object of class org.apache.commons.lang3.reflect.TypeUtils.ParameterizedTypeImpl
        final Type apacheType = TypeUtils.parameterize(Map.class, String.class, Integer.class);

        // These two objects are equal...
        Assert.assertTrue(openJdkType.equals(apacheType));
        Assert.assertTrue(apacheType.equals(openJdkType));

        // ... but their hash codes are not the same
        Assert.assertFalse(openJdkType.hashCode() == apacheType.hashCode());
    }
{code}

> 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)

Mime
View raw message