我在使用泛型时遇到编译错误



我编写以下代码:

 public void test() {
  Callable<?> myCall = new Callable() {
    @Override
    public String call() throws Exception {
        return doDomething();
    }
};

Callable<?> myCall2 = new Callable() {
    @Override
    public String call() throws Exception {
        return doDomething2();
    }
};
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Future<?>> futuresList = executor.invokeAll((Collection<? extends Callable<?>>) getList());
String result1 = futuresList.get(0).get();
String result2 = futuresList.get(0).get();
...
...
}
private List<Callable<?>> getList() {
    .. create callables with wildcard and return them
}

我得到了以下编译错误:类型ExecutorService中的invokeAll(Collection>)方法不适用于参数(Collection>.)。

编辑我添加了一个方法getList,因为我希望它使用泛型而不是String。我想知道为什么它不能编译。在我的实际程序中,它是一种方法。

您必须了解泛型中何时需要通配符。在你的例子中,你根本不需要它们。只有当您不知道某个泛型对象的类型时,才需要它们。在您的示例中,您希望Callables返回Strings,因此您应该将其用作泛型类型,如下所示:

public void test() throws InterruptedException, ExecutionException {
  Callable<String> myCall = new Callable<String>(){
    public String call() throws Exception{
      return doDomething();
    }
  };

  Callable<String> myCall2 = new Callable<String>(){
    public String call() throws Exception{
      return doDomething2();
   }
  };
  ExecutorService executor = Executors.newFixedThreadPool(2);
  List<Callable<String>> list = Arrays.asList(myCall, myCall2);
  List<Future<String>> futuresList = executor.invokeAll(list);
  String result1 = futuresList.get(0).get();
  String result2 = futuresList.get(1).get();
  executor.shutdown();
}

请参阅Sun的/Oracle关于泛型和通配符的教程

编辑:

  • futuresList.get(0)更改为futuresList.get(1)以获得第二个结果
  • 添加了executor.shutdown(),因为人们往往会忘记这一点

第2版:

针对您的评论,这里是另一个例子。这是你可以做这样事情的一种方式。我想展示如何在Callables内部从调用者生成所有涉及的方法,或者更好地生成doDomething方法。

public void test() throws Exception {
  ExecutorService executor = Executors.newFixedThreadPool(2);
  List<Callable<String>> list = getList();
  List<Future<String>> futuresList = executor.invokeAll(list);
  String result1 = futuresList.get(0).get();
  String result2 = futuresList.get(1).get();
  System.out.println(result1);
  System.out.println(result2);
  executor.shutdown();
}
private <T> List<Callable<T>> getList() {
  Callable<T> myCall = new Callable<T>(){
    public T call() throws Exception{
      return (T) "a"; //doDomething();
    }
  };
  Callable<T> myCall2 = new Callable<T>(){
    public T call() throws Exception{
      return (T) "b"; //doDomething2();
    }
  };
  return Arrays.asList(myCall, myCall2);
}

使用invokeAll()删除行中的强制转换,并使用String更改未绑定的通配符(?)。这是不可编译的,这是可能的,但很难确切解释为什么,但是,既然你真的打算在String上操作,就这么说吧,它就起作用了。

我同意发布的其他答案-您应该只是在String上参数化,而不是通配符。

如果你想知道为什么通配符不能像你预期的那样工作,Angelika Langer出色的Java泛型常见问题解答中有一节介绍了重复通配符声明(这是第二个例子)。

最新更新