gpt4 book ai didi

java - 为什么这种通用分配是非法的?

转载 作者:行者123 更新时间:2023-12-03 10:04:10 26 4
gpt4 key购买 nike

我有一个类:

class Generic<T> {
List<List<T>> getList() {
return null;
}
}
当我使用通配符声明 Generic并调用 getList方法时,以下分配是非法的。
Generic<? extends Number> tt = null;
List<List<? extends Number>> list = tt.getList(); // this line gives compile error
这对我来说似乎很奇怪,因为根据 Generic的声明,很自然地创建一个 Generic<T>并在调用 List<List<T>>时获取一个 getList
实际上,这需要我编写这样的作业:
List<? extends List<? extends Number>> list = tt.getList(); // this one is correct
我想知道为什么第一个是非法的,为什么第二个是合法的。
我给出的示例只是一些示例代码来说明问题,您不必关心它们的含义。
错误信息:

Incompatable types:
required : List<java.util.List<? extends java.lang.Number>>
found: List<java.util.List<capture<? extends java.lang.Number>>>

最佳答案

这是您遇到的关于通配符类型的棘手但有趣的事情!当您理解它时,它很棘手,但确实合乎逻辑。
该错误与以下事实有关:通配符? extends Number不引用一种具体类型,而是引用某种未知类型。因此,两次出现的? extend Number不一定引用相同的类型,因此编译器无法允许该赋值。
详细说明

  • 分配的右侧tt.getList()没有获得List<List<? extends Number>>类型。而是由编译器为它的每次使用分配一个唯一的生成的捕获类型,例如称为List<List<capture#1 extends Number>>
  • 捕获类型List<capture#1 extends Number>List<? extends Number>的子类型,但它不是同一类型! (这是为了避免将不同的未知类型混合在一起。)
  • 分配中左侧的类型为List<List<? extends Number>>。此类型不允许List<? extends Number>的子类型成为外部列表的元素类型,因此getList的返回类型不能用作元素类型。
  • 另一方面,List<? extends List<? extends Number>>类型的确允许List<? extends Number>的子类型作为外部列表的元素类型。因此,这是解决该问题的正确方法。

  • 动机
    下面的示例代码演示了为什么分配是非法的。通过一系列步骤,我们最终得到了一个 List<Integer>,其中实际上包含 Float!
    class Generic<T> {
    private List<List<T>> list = new ArrayList<>();

    public List<List<T>> getList() {
    return list;
    }
    }

    // Start with a concrete type, which will get corrupted later on
    Generic<Integer> genInt = new Generic<>();

    // Add a List<Integer> to genInt.list. This is not necessary for the
    // main example but migh make things a little clearer.
    List<Integer> ints = List.of(1);
    genInt.getList().add(ints);

    // Assign to a wildcard type as in the question
    Generic<? extends Number> genWild = genInt;

    // The illegal assignment. This doesn't compile normally, but we force it
    // using an unchecked cast to see what would happen IF it did compile.
    List<List<? extends Number>> list =
    (List<List<? extends Number>>) (Object) genWild.getList();

    // This is the crucial step:
    // It is legal to add a List<Float> to List<List<? extends Number>>.
    // list refers to genInt.list, which has type List<List<Integer>>.
    // Heap pollution occurs!
    List<Float> floats = List.of(1.0f);
    list.add(floats);

    // notInts in reality is the same list as floats!
    List<Integer> notInts = genInt.getList().get(1);

    // This statement reads a Float from a List<Integer>. A ClassCastException
    // is thrown. The compiler must not allow us to end up here without any
    // previous type errors or unchecked cast warnings.
    Integer i = notInts.get(0);
    您发现的修补程序是对 list使用以下类型:
    List<? extends List<? extends Number>> list = tt.getList();
    这种新类型将类型错误从 list的分配转移到对 list.add(...)的调用。
    上面说明了通配符类型的全部要点:跟踪在何处安全读取和写入值,而不会混淆类型并获得意外的 ClassCastException
    一般经验法则
    当您使用通配符嵌套类型参数时,对于这种情况有一条一般的经验法则:

    If the inner types have wildcards in them, then the outer types often need wildcards also.


    否则,内部通配符就无法像您所看到的那样“生效”。
    引用
    Java Tutorial包含有关捕获类型的一些信息。
    这个问题的答案包含有关通配符的一般信息:
    What is PECS (Producer Extends Consumer Super)?

    关于java - 为什么这种通用分配是非法的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64097022/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com