[TYPES] Java generics unsoundness?

Erik Ernst eernst at daimi.au.dk
Mon Oct 2 18:13:07 EDT 2006


On Monday 02 October 2006 21:39, Pete Kirkham wrote:
> [..]
> The section on override-equivalent signatures,
><http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.2>,
> indicates the effect is intentional - the erasure of B#compareTo(B)
> overrides A#compareTo(Object) as well as Comparable<T>#compareTo(T)
> since B#compareTo(B)  is override equivalent to A#compareTo(Object) 
> by 
> erasure.

True, it is intentional that a generified version of a method should 
be able to be overriden by an erased version, such that people can 
continue to use old code with new, generified libraries.  So dispatch 
does at times select the most specific method implementation from a 
set of methods with different (though override-equivalent) 
signatures:  In 8.4.2 the set is '<T> toList(Collection<T>)' in 
CollectionConverter and 'toList(Collection)' in Overrider, and the 
latter overrides the former.

But 'compareTo(Object)' in A and 'compareTo(B)' in B are not 
override-equivalent (B is a class, not a type variable), so 
override-equivalence does not allow these methods to override each 
other, only overloading may occur.

So far it looks like a bug in the implementation that they end up 
overriding each other, caused by the use of bridge methods.

Now the interesting point is that the crucial bridge method is 
introduced because some clients might access an instance of B under 
the type Comparable<B>.  This interface declares a method with 
signature 'compareTo(B)' where B is a type variable---so it erases to 
'compareTo(Object)' which is of course override-equivalent with 
'compareTo(Object)' in A.

Hence, the two methods 'compareTo(Object)' in A and 'compareTo(B)' in 
B are brought together in overriding _via_ the interface 
Comparable<B> and its method 'compareTo(B)'.  The problem arises 
because B is considered as a class in one context (in the class B) 
and as a type variable in the other context (when accessing an 
instance of B under the type Comparable<B>).  The bridge method is 
generated under the assumption that it will _only_ receive 
invocations based on the type Comparable<B> (and the argument will 
then be of type B), but it may in fact also receive invocations based 
on the type A (with arguments of type Object), which causes the 
ClassCastException.

In other words, the assumptions used when generating bridge methods 
(with the same name) do not always hold.  To me it still seems like 
the problem would disappear if the bridge method were generated with 
a signature along the lines of 'compareTo$B(Object)' and invocations 
based on the type Comparable<B> would call that method.

> Since B's method does override A's method, but takes a takes a B, 
> the 
> object undergoes assignment conversion
><http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.2>,
> and being incompatible, a ClassCastException is thrown. This is
> achieved via the bridging method.

I have made an attempt to argue above that B's method does not 
override A's method.

> So I don't believe it's a bug in the compilers, but rather one of
> several design features of a Java generics that are intended to help
> backwards compatibility. 

I agree that the whole idea about override-equivalence was introduced 
in order to improve flexibility in a gradual 'generification' of 
large bodies of software, but I think that the problem is the 
assumptions behind bridge method generation, rather than the notion 
of override-equivalence.

  best regards,

-- 
Erik Ernst                        eernst at daimi.au.dk
Department of Computer Science, University of Aarhus
IT-parken,  Aabogade 34,  DK-8200 Aarhus N,  Denmark


More information about the Types-list mailing list