Difference | Arrays | Lists |
1 | Covariant | Invariant |
2 | Reified at runtime | Erased at run time |
3 | Runtime type safety | Compile time type safety |
non-reifiable types | E, List<E> and List<String> | whose runtime representation contains less information than its compile-time representation. |
Reifiable types | List<?> and Map<?,?> | Reverse to above. |
This code fragment is legal:
// Fails at runtime!
Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
but this one is not:
// Won't compile!
List<Object> ol = new ArrayList<Long>(); // Incompatible types
ol.add("I don't fit in");
Prohibition on generic array creation
- Not possible for a generic type to return an array of its element type.
- Not possible for using varargs methods in combination with generic types.( This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. Use warning suppress to deal with it if it's required.).
None of these array creation expressions are legal:
new List<E>[],
new List<String>[] ,
new E[].
All will result in generic array creation errors at compile time.
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
Let's pretend that line 1, which creates a generic array, is legal. Line 2 creates and initializes a List<Integer>containing a single element. Line 3 stores the List<String>array into an Object array variable, which is legal because arrays are covariant. Line 4 stores the List<Integer>into the sole element of the Object array, which succeeds because generics are implemented by erasure: the runtime type of a List<Integer>instance is simply List, and the runtime type of aList<String>[]instance is List[], so this assignment doesn't generate an ArrayStoreException. Now we're in trouble. We've stored a List<Integer> instance into an array that is declared to hold only List<String>instances. In line 5, we retrieve the sole element from the sole list in this array. The compiler automatically casts the retrieved element to String, but it's an Integer, so we get a ClassCastExceptionat runtime. In order to prevent this from happening, line 1 (which creates a generic array) generates a compile-time error.
Use the Lists instead of the array to check the type safety at compile time
// List-based generic reduction
static <E> E reduce(List<E> list, Function<E> f, E initVal) {
List<E> snapshot;
synchronized(list) {
snapshot = new ArrayList<E>(list);
}
E result = initVal;
for (E e : snapshot)
result = f.apply(result, e);
return result;
}
Summary
If you find yourself mixing them and getting compile-time errors or warnings, your first impulse should be to replace the arrays with lists.