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);
}
f1
works because of type erasure. The cast to E[]
is completely removed at runtime, meaning that your code would behave something like this at runtime, but it does let the compiler infer that you're returning a List<E>
and not a List<Object>
, therefore letting the program compile.
static List f1(List list) {
return List.of(list.toArray());
}
f2
works because of basically the same reason. This time, the result of List.of(list.toArray())
is inferred to be a List<Object>
because toArray
returns an Object[]
, but the cast, which is later erased, makes it compile.
f1a
has a ClassCastException
because you're trying to turn an Object[]
into a String[]
. In Java, arrays know the type they were declared with even at runtime, so the array returned by toArray
thinks of itself as an Object[]
even though it really only holds strings and doesn't like being turned into a String[]
.
f2a
doesn't work because you can't turn a List<Object>
into a List<String>
.
If you want to turn your list into an array of strings, try list.toArray(new String[0])
. This way, it will return a proper String[]
and not cause problems.
If you convert a List<Object>
to a List<String>
, cast to a raw type with (List) List.of(list.toArray())
f1a
case:
Here, the explicit casting is done, which means that compiler expects the same type at the compile time. By providing explicit cast (String[]) list.toArray()
, you tell to your compiler, that whatever is returned by .toArray()
, it should attempt to cast it to String[]
, and hence you want to build a List.of(String[])
, which also constitutes a return type of your method. This is why it doesn't have any compile-time problem, but rather it throws run-time exception, as it can't cast Object
to String
. Think of it as at the compile time, explicitly given instruction is correct for the compiler, but it doesn't work at run-time. To understand more about casting, please also see Checked Casts at Runtime and maybe also type erasure .
f2a
case:
.toArray()
method of ArrayList
, by method definition returns Object[]
. That's why you get the list of objects, which can't be downcasted to list of Strings, and this happens, because it can't cast it and this is a checked cast , and you get compile-time Exception, ie your code doesn't compile.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.