I know the general differences between generic types and parameterized types, and I know some general rules:
List<A> and List<B> have no inheritance relationship, even if A and B are related through an inheritance chain;
Object[] cannot be cast to String[], unless the Object[] array was constructed using new String[n].
But my question is a bit specific. So I am going to give some code.
According to the above general rules, the cast in the following 2 examples is invalid:
static List<String> f1a(List<String> list) {
return List.of((String[]) list.toArray()); // ClassCastException
}
static List<String> f2a(List<String> list) {
return (List<String>) List.of(list.toArray()); // compile-time error: Inconvertible types
}
Now if I replace the String type with a generic type parameter E, the casting works! But I really don't understand why?
// f1 is a generic version f1a, where String -> E
static <E> List<E> f1(List<E> list) {
return List.of((E[]) list.toArray());
}
// f2 is a generic version f2a, where String -> E
static <E> List<E> f2(List<E> list) {
return (List<E>) List.of(list.toArray());
}
The following demo shows that f1 and f2 are valid, while f1a and f2a are problematic:
public static void main(String[] args) {
List<String> list = List.of("hello", "world");
List<String> copy1 = f1(list); // works
System.out.println(copy1);
List<String> copy2 = f2(list); // works
System.out.println(copy2);
List<String> copy1a = f1a(list); // ClassCastException
System.out.println(copy1a);
List<String> copy2a = f2a(list); // compile-time error
System.out.println(copy2a);
}