将元素添加到wildchar列表会导致编译错误

时间:2013-04-25 03:32:56

标签: java generics

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

这是代码:

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

编译错误:

  

add(Pair<String,Future<?>>)类型中的方法List<Pair<String,Future<?>>>不适用于参数(Pair<String,Future<String>>)

1 个答案:

答案 0 :(得分:5)

您收到错误是因为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> 1}}。

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

pairOfStringAndSomethingThatIsAFutureOfSomething = pairOfStringAndFutureOfString;

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

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

要详细了解这些限制,请参阅此帖子:What is PECS (Producer Extends Consumer Super)?