Java 泛型:通配符捕获误解

2022-09-01 18:19:38

阅读Java在线教程,我对通配符捕获一无所知。例如:

    import java.util.List;
    public class WildcardError {
     void foo(List<?> i) {
      i.set(0, i.get(0));
     }
    }

为什么编译器无法安全地保留赋值?

它知道,例如,通过执行带有List的方法,它从值获取。因此,它尝试将索引处的值设置为相同的整数列表()。Integeri.getIntegerInteger0i

那么,这是怎么回事呢?为什么要写通配符帮助程序?


答案 1

为什么编译器不能安全地保留赋值?它知道,例如,通过执行具有整数列表的方法,它从i.get一个整数值中获取。因此,它尝试将索引 0 处的 Integer 值设置为同一 Integer 列表 (i)。

换句话说,为什么编译器不知道通配符类型的两种用法List<?>

i.set(0, i.get(0));

指的是相同的实际类型?

好吧,这将要求编译器知道包含表达式的两个计算的相同实例。由于甚至不是最终的,编译器必须检查是否可以在计算两个表达式之间分配。这样的分析只对局部变量很简单(谁知道调用的方法是否会更新特定对象的特定字段?这在编译器中是相当多的额外复杂性,因为很少表现出好处。我想这就是为什么Java编程语言的设计者通过指定同一通配符类型的不同用法具有不同的捕获来保持简单的原因。iii


答案 2

为什么编译器不能安全地保留赋值?

编译器对 中的元素类型一无所知,根据 的定义。通配符表示“任何类型”;它表示“某种未知类型”。List<?> i?

它知道,例如,通过执行具有整数列表的方法,它从i.get一个整数值中获取。

这是真的,但正如我上面所说:编译器只能知道 - 在编译时,记住 - 返回一个,这是 的上限。但是不能保证是在运行时,所以编译器没有办法知道这是一个安全的调用。就像这样写:i.get(0)Object??Objecti.set(0, i.get(0))

List<Foo> fooz = /* init */;
Object foo = fooz.get(0);
fooz.set(0, foo); // won't compile because foo is an object, not a Foo

更多阅读:


推荐