简体   繁体   English

Java 中的泛型类型转换与参数化类型转换

[英]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; List<A> 和 List<B> 没有 inheritance 关系,即使 A 和 B 通过 inheritance 链相关;

  • Object[] cannot be cast to String[], unless the Object[] array was constructed using new String[n]. Object[] 不能转换为 String[],除非 Object[] 数组是使用 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:根据上述一般规则,以下 2 个示例中的强制转换无效:

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?现在,如果我用泛型类型参数 E 替换 String 类型,转换是否有效? 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:下面的demo显示f1和f2是有效的,而f1a和f2a是有问题的:

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. f1由于类型擦除而起作用。 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.E[]的强制转换在运行时被完全删除,这意味着你的代码在运行时会表现得像这样,但它确实让编译器推断你返回的是List<E>而不是List<Object> ,因此让程序编译。

static List f1(List list) {
  return List.of(list.toArray());
}

f2 works because of basically the same reason. f2的工作原理基本相同。 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.这一次, List.of(list.toArray())的结果被推断为一个List<Object>因为toArray返回一个Object[] ,但是后来被擦除的强制转换使其编译。


f1a has a ClassCastException because you're trying to turn an Object[] into a String[] . f1a有一个ClassCastException因为您试图将Object[]转换为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[] .在 Java 中,arrays 即使在运行时也知道它们声明的类型,因此toArray返回的数组将自己视为Object[]即使它实际上只包含字符串并且不喜欢变成String[]

f2a doesn't work because you can't turn a List<Object> into a List<String> . f2a不起作用,因为您无法将List<Object>变成List<String>

If you want to turn your list into an array of strings, try list.toArray(new String[0]) .如果要将列表转换为字符串数组,请尝试list.toArray(new String[0]) This way, it will return a proper String[] and not cause problems.这样,它将返回正确的String[]而不会引起问题。

If you convert a List<Object> to a List<String> , cast to a raw type with (List) List.of(list.toArray())如果将List<Object>转换为List<String> ,请使用(List) List.of(list.toArray())转换为原始类型

f1a case: f1a案例:

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.通过提供显式(String[]) list.toArray() ,您告诉编译器,无论.toArray()返回什么,它都应该尝试将其转换为String[] ,因此您想要构建一个List.of(String[]) ,它也构成了您的方法的返回类型。 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 .这就是为什么它没有任何编译时问题,而是抛出运行时异常,因为它不能将ObjectString 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: f2a案例:

.toArray() method of ArrayList , by method definition returns Object[] . ArrayList.toArray()方法, 通过方法定义返回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.这就是为什么你得到对象列表,它不能被向下转换为字符串列表,这会发生,因为它不能转换它,这是一个检查转换,你得到编译时异常,即你的代码没有不编译。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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