使用钻石操作符的通配符

7
如果我想做这样的事情:
List<?> unknownList = new ArrayList<>();

如果代码编译并运行良好,但是ArrayList创建的是哪种类型?

在这一行之后,如果我像这样做:

        unknownList.add("str"); //compilation error

它会显示编译错误:

error: no suitable method found for add(String)
        unList.add("str");
              ^
method List.add(int,CAP#1) is not applicable
  (actual and formal argument lists differ in length)
method List.add(CAP#1) is not applicable
  (actual argument String cannot be converted to CAP#1 by method invocation conversion)
method Collection.add(CAP#1) is not applicable
  (actual argument String cannot be converted to CAP#1 by method invocation conversion)
where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?

这个错误是什么,使用通配符的钻石操作符好吗?如果是,那么在哪里使用?

ArrayList 没有不同的类型需要创建。类型参数仅存在于编译时。 - Dawood ibn Kareem
3个回答

4

但 ArrayList 是用哪种类型创建的呢?

类型参数仅在编译时应用的约束条件,但类型擦除将所有类型参数的出现替换为其擦除(在您的情况下为Object)。因此,如果您询问运行时类型,则将是普通的ArrayList(您可以将其视为ArrayList<Object>)。

在使用通配符时是否使用钻石操作符好?如果是,请问在哪里使用?

不。当您使用钻石操作符创建泛型类的新对象时,这意味着您不想在类型参数上重复。它不应该与通配符声明变量结合使用,因为它没有具体的类型参数。

总之,您不应该编写:

List<?> unknownList = new ArrayList<>();

当类型真正无关紧要时,您应该只使用通配符<?>。特别是,如果您想向列表中添加项目,请不要使用通配符,因为添加项目意味着您知道要添加什么类型

例如,它可能被用作方法的参数,当您不访问值而只是传递列表时,或者当您只访问列表项作为普通对象时。


您并没有真正回答这个问题(使用带有通配符的菱形运算符好不好),您只是在一般情况下解释通配符和泛型。 - magnum87

2
你的错误在于,你有一个类型为List<?>的变量,并在它上调用add并传递了一个String
如果你的变量是List<String>类型,那么你可以这样做。但是因为它只是List<?>类型,编译器无法确定是否可以将String添加到变量引用的列表中。
如果要存储字符串,请确保有一个List<String>(或其他字符串集合)类型的变量来存储它们。
使用钻石操作符创建将存储在带有通配符的类型变量中的东西是完全可以的。 钻石操作符对运行时获得的内容没有影响;而且在运行时,没有类型参数。 你使用的代码行创建对象后,对该对象执行的任何后续操作都将根据变量类型在编译时进行检查,而不是根据代码行进行检查。

0
但是ArrayList创建了哪种类型的对象呢?
可能是ArrayList。可能是ArrayList。可能是ArrayList。实际上并不重要,因为它们之间在编译后的代码中没有任何区别,而且你只会得到一个List,所以你不能假设类型参数是什么。
使用通配符的钻石操作符好吗?
不好。对于一个新创建的泛型对象来说,使用通配符参数化的引用是没有意义的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接