I would disagree that there are no checks at Runtime - but it is true that all type-checking is done at compile time.
It's always interesting to look at the actual bytecode produced. Let us take this code
import java.util.*;
class Types{
public static void main(String [] args){
List<String> list = new ArrayList<String>();
list.add("hello");
System.out.println(list.get(0));
}
}
Compile it and then disassemble it using javap -c
, we get
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String hello
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
20: aload_1
21: iconst_0
22: invokeinterface #7, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
27: checkcast #8 // class java/lang/String
30: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return
}
We notice a two things
- As expected, the list is a list of Objects
- On line 27 we call
checkcast
to ensure that the object retrieved from the list is in fact a String
. This is a runtime check inserted by the compiler. Something that would have been manually introduced prior to Generics.