简体   繁体   中英

Generic type casting vs parameterized type casting in Java

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM