Java Generics:通配符捕获误解

19 java generics wildcard

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

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

为什么编译器不能保持赋值安全?它知道,通过执行例如带有整数列表的方法,它从i.get得到一个Integer值.因此,它尝试将索引0处的Integer值设置为相同的整数列表(i).那么,出了什么问题?为什么要写Wildcard助手呢?

mer*_*ike 22

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

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

i.set(0, i.get(0));
Run Code Online (Sandbox Code Playgroud)

是指相同的实际类型?

那么,这将要求编译器知道i包含表达式的两个评估的相同实例.由于i甚至不是最终的,编译器必须检查是否i可能在评估两个表达式之间分配.这样的分析只对局部变量很简单(谁知道被调用的方法是否会更新特定对象的特定字段?).这在编译器中非常复杂,因为它很少显示出好处.我想这就是为什么Java编程语言的设计者通过指定相同通配符类型的不同用法具有不同的捕获来使事情变得简单.


Mat*_*all 20

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

编译器不知道任何关于类型中的元素List<?> i通过定义?.通配符并不会意味着"任何类型;" 它意味着"某种未知类型".

它知道,通过执行例如带有整数列表的方法,它从i.get得到一个Integer值.

这是真的,但正如我上面所说:编译器只能知道 - 在编译时,记住 - i.get(0)返回一个Object,这是...的上限?.但是,没有任何保证,?在运行时 Object,所以没有办法让编译器知道i.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
Run Code Online (Sandbox Code Playgroud)

更多阅读:

  • @meriton的答案更正确.编译器不必知道有关元素的实际运行时类型的任何信息.如果在两种情况下都保证`i`是相同的实例,`get`方法的(未知)返回类型将与`set`方法的(未知)第二参数类型相同,因此它将编译.然而,正如meriton指出的那样,这种保证从未被假定(即使`i`是本地的或最终的). (7认同)
  • @rosshjb问题涉及局部变量,在这种情况下,编译器实际上可以推断两个通配符具有相同的类型,即使不知道确切的类型。Meron 的答案澄清了语言设计者选择不这样做,而这个答案明确指出编译器无法知道,这是不正确的。在一个成员变量或两个单独的变量使用通配符的情况下,这是正确的,但对于这个特定问题的情况则不然。 (2认同)