Why double can be added to integer list using reflection

Why does this code work without any exceptions?

public static void main(String args[]) { List<Integer> a = new ArrayList<Integer>(); try { a.getClass() .getMethod("add", Object.class) .invoke(a, new Double(0.55555)); } catch (Exception e) { e.printStackTrace(); } System.out.println(a.get(0)); } 
+60
java
Nov 30 '13 at 13:31
source share
4 answers

Generics are a compile-time thing. At run time, a regular ArrayList is used, without any additional validation. Since you avoid security checks by using reflection to add items to your list, nothing can prevent Double being stored inside your List<Integer> . Just as if you were doing

 List<Integer> list = new ArrayList<Integer>(); List rawList = list; rawList.add(new Double(2.5)); 

If you want your list to perform type checks at runtime, use

 List<Integer> checkedList = Collections.checkedList(list, Integer.class); 
+93
Nov 30 '13 at 13:38
source share

Due to type erasing - there are no runtime checks for generics; during compilation, type parameters are deleted: Java generics - erase styles - when and what happens .

You may be surprised, but you do not need to use reflection to add Double to the List<Integer> :

 List<Integer> a = new ArrayList<Integer>(); ((List)a).add(new Double(0.555)); 
+35
Nov 30 '13 at 13:38
source share

The reason for this is the erasure type : the fact that this is an Integer list is known to the compiler, not the JVM.

After compiling the code, List<Integer> becomes a List<Object> , allowing code based on reflection without errors.

Please note that your own code has a strong hint of why this works:

 a.getClass() .getMethod("add", Object.class) // <<== Here: Object.class, not Integer.class .invoke(a, new Double(0.55555)); 

Also note that you can achieve the same evil result through some creative use of casting, without reflection. All this is the result of a design solution for implementing Java generics with type-wiping.

+23
Nov 30 '13 at 13:39
source share

Generics is just the compilation time provided by java. Prior to generics, it was not possible to verify that at compile time the instance of the "Object" that you get from the collection is actually of the type that you expect. We would have to apply the object to the appropriate type to make it suitable for use in the code, and this can be risky, since only at runtime the JVM complains about ClassCastException . There was nothing at compile time to protect us from this.

Generics solved this problem by checking type checks in collections at compile time. But another important thing about generics is that they do not exist at run time. If you decompile a class containing a collection of types, such as List or Map, and look at the source obtained from it, you will not find your collective collection declaration there. Since the reflection code runs at runtime and does not have time to compile, you do not get an exception there. Try the same thing at compile time with the normal put or add operation and you will get a compile-time error.

+5
Nov 30 '13 at 13:46
source share



All Articles