[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