commons-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Sebb (JIRA)" <>
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 23:28:04 GMT


Sebb commented on LANG-1323:

I see what you mean now. 
It seems that there are additional equals/hashCode requirements for instances of the Type
interface hierarchy which go beyond the normal equals/hashCode contract.
Unfortunately the requirements don't appear to be fully documented.

The ParameterizedType Javadoc specifies how the equals() method is to be implemented, but
it does not say anything about the hashCode requirements.

The variables to be compared are known, so the equals() method is easily written from the

However there are potentially multiple ways to derive the hashCode.
I don't see how it's possible to code alternate implementations from the Javadoc alone.
So it looks to me as though the Javadoc is incomplete.
If the OpenJDK source were not available it would be impossible to implement an object that
conforms to the ParameterizedType Javadoc and the equals/hashCode contract.

> Type implementations in TypeUtils compute hash code that breaks Object.equals() with
Sun's OpenJDK
> --------------------------------------------------------------------------------------------------
>                 Key: LANG-1323
>                 URL:
>             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|]
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

View raw message