如果我想做这样的事情:
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 ?
这个错误是什么,使用带有通配符的钻石操作符是否合适?如果是,那么WHERE ???
答案 0 :(得分:4)
但是ArrayList的类型是什么?
类型参数只是在编译时应用的约束,但 type erasure 通过擦除替换所有类型参数(在您的情况下为Object
)。因此,如果您要询问运行时类型,那么它将是一个简单的ArrayList
(您可以将其视为ArrayList<Object>
)。
将钻石操作符与通配符一起使用是否合适?如果是,那么 WHERE ???
没有。当您使用菱形运算符创建泛型类的新对象时,这意味着您不希望在类型参数上出现冗余。 不应该与通配符声明的变量结合使用,该变量没有具体的类型参数。
总结一下,你永远不应该写:
List<?> unknownList = new ArrayList<>();
当类型无关紧要时,您应该只使用通配符<?>
。特别是,如果您要将项目添加到列表中,请不要使用通配符,因为添加项目意味着您知道要添加的类型。
例如,当您不访问该值并且只是传递列表,或者您只是以普通对象的形式访问列表项时,它很可能被用作方法的参数。
答案 1 :(得分:2)
您的错误是您拥有List<?>
类型的变量,您在其上调用add
并传递String
。
如果您的变量属于List<String>
类型,您可以这样做 - 但由于它只是List<?>
类型,编译器无法知道将String
添加到List<String>
是否合法无论变量引用什么列表。
如果你要存储字符串,那么请确保你有一个{{1}}类型的变量(或其他一些字符串集合)来存储它们。
使用菱形运算符来创建将存储在其类型中带有通配符的变量中的内容非常好。钻石操作员对您在运行时获得的内容没有任何影响;在运行时,没有类型参数。您在创建对象时执行的任何后续操作都将在编译时针对变量的类型进行检查,而不是针对您用于创建对象的代码行。
答案 2 :(得分:0)
但是ArrayList创建了哪种类型?
可能是ArrayList<Object>
。它可能是ArrayList<String>
。它可能是ArrayList<SomeBogusUnrelatedClass>
。这无关紧要,因为无论如何它们之间的编译代码没有区别,你得到的只是List<?>
所以你不能假设类型参数是什么。
将钻石操作符与通配符一起使用是否合适?
不。对新创建的泛型对象进行通配符参数化引用是没有意义的。