THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

}


and the following incorrect use:


public static void main(String[] args) {
PassThrough s = new PassThroughString();
s.passThrough(args);
}


This results in an "unchecked" warning at compile time because the use of the raw type means that we don't
know what type passThrough should take or return. But we can see that s.passThrough should only be
accepting a String argument, and args is a String[] not a String. If the compiler rejected the above,
we could appease the compiler by casting args to String, and then we should expect, and would get, a
ClassCastException at runtime. However, the compiler doesn't reject this code, but in fact if we
execute this method a ClassCastException is still thrown. This behavior is specified as a special rule of
method invocation that deals with this situation in JLS 15.12.4.5:


If the erasure of the type of the method being invoked differs in its signature from the erasure
of the type of the compile-time declaration for the method invocation, then if any of the
argument values is an object which is not an instance of a subclass or subinterface of the
erasure of the corresponding formal parameter type in the compile-time declaration for the
method invocation, then a ClassCastException is thrown.

In simple terms, the compiler has to accept incorrect invocations like the above at compile time, but it has to
ensure that at runtime the bad argument is not actually passed to the method. The ClassCastException
must be thrown before invoking the method (as if we had put in the bad cast ourselves). One way a compiler
can ensure this is to introduce what is known as a bridge method. A suitable bridge method in this case would
be the following:


public Object passThrough(Object o) {
return passThrough((String)o);
}


The compiler would insert calls to this bridge method, rather than calls to the actual method. Thus, the cast
will fail when it should and succeed otherwise. Because bridge methods are introduced by the compiler, they
will be marked as synthetic.


Bridge methods also fill another role in maintaining backward compatibility. For example, prior to generics
the Comparable interface's compareTo method took an Object argument, but now it takes a T,
whatever that may be. However, code compiled against non-generified Comparable implementations have
byte codes to invoke a version of the method that takes an Object. No such version is defined in the source
code, but the compiler generates a compareTo(Object) bridge method that casts the argument to the
expected type and invokes the new compareTo method.


The use of raw types in code written after generics was added is strongly discouraged, as they exist only for
backward compatibility and may be removed in a future version of the language.

Free download pdf