在以下代码中,调用get()
并将其结果分配给类型为List<List<?>>
的变量。 get()
返回List<List<T>>
,并在类型参数T
设置为?
的实例上调用,因此它应该适合。
import java.util.List;
class Test {
void foo(NestedListProducer<?> test) {
List<List<?>> a = test.get();
}
interface NestedListProducer<T> {
List<List<T>> get();
}
}
但IntelliJ IDEA和Oracle的javac
版本1.7.0_45都拒绝我的代码无效。这是'javac'的错误消息:
java: incompatible types
required: java.util.List<java.util.List<?>>
found: java.util.List<java.util.List<capture#1 of ?>>
为什么这段代码无效,即如果允许则会出错?
答案 0 :(得分:7)
?
是一个通配符,意思是任何类型。一个?
不能与另一个?
相同,因为另一个?
可能是任何其他类型,并且它们不匹配。您必须使用泛型来表示类型相同:
// Make this generic
<A> void foo(NestedListProducer<A> test) {
List<List<A>> a = test.get();
}
答案 1 :(得分:4)
List<List<T>>
表示您可以阅读List<T>
或撰写新List<T>
的列表,同样List<List<?>>
表示您可以阅读的列表{{ 1}}来自或写新的List<?>
。关于List<T>
的奇怪之处在于您可以将任何类型?
的列表转换为S
。例如,你可以写:
List<?>
如果您可以将void foo(List<String> a, List<Integer> b, List<List<?>> out) {
List<?> unknownA = a;
List<?> unknownB = b;
out.add(a);
out.add(b);
}
转换为List<List<T>>
,则可以使用List<List<?>>
调用foo
,然后向其中添加字符串和整数列表。
一般来说,人们会遇到这种情况,因为他们试图表达他们想要一些类型无关紧要的子集合的概念。如果这是您想要的,您可以将类型从List<List<PeanutButter>>
更改为List<List<?>>
,这表示我可以读取但不能写入的子列表列表的概念。将List<? extends List<?>>
转换为List<List<T>>
。
答案 2 :(得分:4)
您似乎对编译器如何处理List<?>
和List<List<?>>
感到困惑。 List<?>
是某种未知的 List
,而List<List<?>>
是 List
List
的(不同类型的未知)。
因此,对于List<?>
,通配符?
表示单个未知类型,因此编译器会将其捕获到该单个未知类型的占位符。在List<List<?>>
中,通配符?
表示不同的未知类型。编译器不会捕获这样的类型,因为不同的未知类型不能有一个占位符。
现在考虑你原来的例子:
void foo(NestedListProducer<?> test) {
List<List<?>> a = test.get();
}
在这种情况下,编译器将捕获?
NestedListProducer
以在编译时创建一个匿名类型参数,并将创建一个辅助方法,如:
<CAP#1 of ?> void foo_2(NestedListProducer<CAP#1 of ?> test) {
List<List<?>> a = test.get();
}
(注意:它不会捕获?
中的List<List<?>>
,因此它会保持原样)。
现在,test.get()
的返回类型为List<List<CAP#1 of ?>>
。哪个不是来自List<List<?>>
的赋值捕获可转换,因此无法将其赋值给它。因此无法编译。
因此,解决方法是自己添加类型参数,如已建议的那样:
<T> void foo(NestedListProducer<T> test) {
List<List<T>> a = test.get();
}
从评论中查询:
现在正如您在评论中提到的,为什么以下代码有效?
void foo(List<List<?>> arg) {
List<List<?>> a = arg;
}
从上面的解释中,您可以猜测不会捕获形式参数中?
中的通配符List<List<?>>
。因此,分配实际上是从List<List<?>>
到List<List<?>>
,这是有效的。这里没有CAP#1 of ?
。
答案 3 :(得分:0)
您可以使用此技巧(通配符捕获)
void foo(NestedListProducer<?> test) {
foo2(test);
}
<T> void foo2(NestedListProducer<T> test) {
List<List<T>> a = test.get();
}