I am stuck with a code that I do not understand why it works. Assume that I create a generic interface Foo<T>
as following:
interface Foo<T>{
void set(T item);
}
Then I create a class called Bar
which implements Foo<String>
as following:
class Bar implements Foo<String>{
@override
public void set(String item){
//useless body
}
}
Based on this we can writes following code:
Bar bar = new Bar();
bar.set("Some string");
Foo rawFoo = (Foo) bar;
rawFoo.set(new Object()); // ClassCastException: Object cannot be cast to string
That last line is the one that I don't really get. As it's known that when using raw types the generic parameter types are converted to Object
.
In this case the code compiles and we can pass Object to set()
method. But how Java determines that it has to cast the Object to String at runtime?
If you decompile Bar
using javap
:
class Bar implements Foo<java.lang.String> {
Bar();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void set(java.lang.String);
Code:
0: return
public void set(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: checkcast #2 // class java/lang/String
5: invokevirtual #3 // Method set:(Ljava/lang/String;)V
8: return
}
void set(java.lang.Object)
is a synthetic bridge method. Note the checkcast
instruction. The equivalent "real" code would look like:
public void set(Object object) {
set((String) object);
}
The compiler created this method in order for the class to work under type erasure; the actual method doing the work is set(java.lang.String)
, to which the bridge method delegates.
It is this bridge method which actually overrides the method in the base class. The method you declare with the @Override
annotation is merely an overload.