javagenericscastingraw-types

Casting to raw type


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?


Solution

  • 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.