Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding typesafety anomaly with Java generics [duplicate]

I have following code:

public static void main(String[] args) {
    List<String> s = new ArrayList<String>();
    s.add("kshitiz");

    //This is not typesafe. It should blow up at runtime
    List<Integer> i = new ArrayList(s);
    System.out.println(i.get(0));
}

This program runs fine and it prints kshitiz. It only fails if I replace the last line with:

System.out.println(i.get(0).getClass());

Exception:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

What is happening here?

like image 934
Kshitiz Sharma Avatar asked Apr 23 '15 15:04

Kshitiz Sharma


1 Answers

I guess you are aware, that generic types are gone at run time. Now, to see what's happening behind the scenes, let's look at this piece of code

public static Class<?> getClass(final Object object) {
    return object.getClass();
}

public static void main(final String[] args) {
    final List<String> s = new ArrayList<String>();
    s.add("kshitiz");

    // This is not typesafe. It should blow up at runtime
    final List<Integer> i = new ArrayList(s);
    System.out.println(getClass(i.get(0)));
    System.out.println(i.get(0).getClass());
}

and the output of javap -c

  [...]
  26: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
  29: aload_2
  30: iconst_0
  31: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  36: invokestatic  #10                 // Method getClass:(Ljava/lang/Object;)Ljava/lang/Class;
  39: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  42: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
  45: aload_2
  46: iconst_0
  47: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  52: checkcast     #12                 // class java/lang/Integer
  55: invokevirtual #2                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
  58: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  61: return

So you see that in the second call, the String is cast to an Integer, while in the first case, it is regarded as an object. Thus as long as the String is handled just like any other object, everything is fine, but as soon as you call a method of the element type of the list, the object is cast to that type.

like image 127
muued Avatar answered Oct 14 '22 21:10

muued