java - 向 wildchar 列表中添加一个元素会导致编译错误

标签 java generics

我有一个包含 future 任务的列表, future 类型未知,所以我创建了一个通配符类型的列表 ? ,但是当我向列表中添加一个元素时,会发生编译错误。

这是代码:

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;
    }}))
);

编译器错误:

The method add(Pair<String,Future<?>>) in the type List<Pair<String,Future<?>>> is not applicable for the arguments (Pair<String,Future<String>>)

最佳答案

你得到一个错误,因为 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;

同样,这是不允许的,因为泛型不是协变的。为什么?考虑一下如果它们是:

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

我们得到了 futureOfString指向实际上是 Future<Integer> 的东西.但这甚至没有导致 ClassCastException因为类型删除,JVM 只看到了 Future。秒。这种情况被称为“堆污染”——一个变量指向一个它不应该被允许指向的对象。实际的运行时错误仅在我们尝试解包 futureOfString 时发生.

这就解释了为什么泛型不是协变的。让我们看看解决方法:

Pair<String, ? extends Future<?>>
pairOfStringAndSomethingThatIsAFutureOfSomething;

这个变量名很啰嗦,但我希望它能准确描述这里发生的事情。之前,第二个类型参数完全 Future<?> .现在,我们说它是“某种未知类型,它是或扩展 Future<?> 。例如,它可能是 Future<?> ,可能是 Future<String> ,甚至可能是 MySpecialFuture<Integer>

我们自愿放弃一些有关此变量类型的信息,以放宽可以分配给它的内容。现在,这是合法的:

pairOfStringAndSomethingThatIsAFutureOfSomething = pairOfStringAndFutureOfString;

编译器通过防止这种未知类型被“消耗”来防止早期的“堆污染”场景:

//compiler error - nothing can be legally passed into setRight
pairOfStringAndSomethingThatIsAFutureOfSomething.setRight(futureOfSomething);

要了解有关这些限制的更多信息,请参阅此帖子:What is PECS (Producer Extends Consumer Super)?

关于java - 向 wildchar 列表中添加一个元素会导致编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16205915/

相关文章:

c# - 没有装箱的通用解析方法

java - 为什么不能 List<? extends Animal> 被替换为 List<Animal>?

java - jhat OQL AND in where 子句

java - 我们什么时候使用接口(interface)扩展接口(interface)

java - 如何在 String.xml 中使用两个具有相同名称的不同字符串值?

java - 为什么我在尝试从 Android 中的字符串解析整数时会收到 NumberFormatException?

具有多个节点的 JavaFx 过渡动画

java - 为什么泛型类型和通配符类型输出结果不同

java - 使用递归类定义创建第一个对象

c# - 为什么通用类型定义实现的接口(interface)会丢失类型信息?