我有一个列表包含未来的任务,未来的类型是未知的,所以我创建一个带通配符类型的列表?,但是当我向列表中添加一个元素时,就会发生编译错误.
这是代码:
private List<Pair<String, Future<?>>> futureTasks = Collections.synchronizedList(
new ArrayList<Pair<String, Future<?>>>(8));
// taskId is a string
futureTasks.add(Pair.makePair(taskId, getExecutors().submit(
new Callable<String>() {
public String call() {
try {
return exportAccountSrcTask(tmpFile); // return a string
} catch (Exception e) {
logger.error("failed to export account src", e);
}
return null;
}}))
);
Run Code Online (Sandbox Code Playgroud)
编译错误:
add(Pair<String,Future<?>>)类型中的方法List<Pair<String,Future<?>>>不适用于参数(Pair<String,Future<String>>)
你得到一个错误,因为a Pair<String, Future<String>>不是Pair<String, Future<?>>.通用类型不是协变的.
但是你可以输入futureTasks为List<Pair<String, ? extends Future<?>>>.然后你就可以添加一个Pair<String, Future<String>>了.
编辑:
为了更好地解释这一点,请考虑尝试将对象分配给变量,而不是将其添加到列表中.如果我们有一个List<T>,我们如果只添加一个对象是一个 T.所以这是同样的情况:
Pair<String, Future<String>> pairOfStringAndFutureOfString;
Pair<String, Future<?>> pairOfStringAndFutureOfSomething;
// incompatible types
pairOfStringAndFutureOfSomething = pairOfStringAndFutureOfString;
Run Code Online (Sandbox Code Playgroud)
同样,这是不允许的,因为泛型不是协变的.为什么?考虑一下如果它们会发生什么:
Future<Integer> futureOfInt;
Future<?> futureOfSomething;
futureOfSomething = futureOfInt; // a future of int is a future of something
Pair<String, Future<String>> pairOfStringAndFutureOfString;
Pair<String, Future<?>> pairOfStringAndFutureOfSomething;
// pretend this is legal
pairOfStringAndFutureOfSomething = pairOfStringAndFutureOfString;
// danger!
pairOfStringAndFutureOfSomething.setRight(futureOfSomething);
Future<String> futureOfString = pairOfStringAndFutureOfString.getRight();
//sometime later...
String string = futureOfString.get(); //ClassCastException
Run Code Online (Sandbox Code Playgroud)
我们必须futureOfString指出实际上是一种东西Future<Integer>.但这甚至没有立即引起ClassCastException,因为由于类型擦除,JVM只看到了Futures.这种情况称为"堆污染" - 变量指向一个它不应该被允许的对象.实际运行时错误仅在我们尝试解包时发生futureOfString.
这就解释了为什么泛型不是协变的.让我们来看看解决方法:
Pair<String, ? extends Future<?>>
pairOfStringAndSomethingThatIsAFutureOfSomething;
Run Code Online (Sandbox Code Playgroud)
这个变量名称是满口的,但我希望它能准确描述这里发生了什么.之前,第二种类型的论点是完全正确的 Future<?>.现在,我们说它是"某种未知的类型,它是或扩展的Future<?>.例如,它可能是Future<?>,Future<String>甚至可能是MySpecialFuture<Integer>.
我们会自愿放弃一些有关此变量类型的信息,以便放宽可分配给它的内容.现在,这是合法的:
pairOfStringAndSomethingThatIsAFutureOfSomething = pairOfStringAndFutureOfString;
Run Code Online (Sandbox Code Playgroud)
编译器通过防止这种未知类型被"消耗"来防止早期的"堆污染"情况:
//compiler error - nothing can be legally passed into setRight
pairOfStringAndSomethingThatIsAFutureOfSomething.setRight(futureOfSomething);
Run Code Online (Sandbox Code Playgroud)
要了解有关这些限制的更多信息,请参阅以下文章:什么是PECS(Producer扩展消费者超级)?
| 归档时间: |
|
| 查看次数: |
90 次 |
| 最近记录: |