1

我遇到了以下代码的一个奇怪问题(嗯,不完全是这段代码):

public class CompilationProblems1 {

  static Box<Class<? extends AlcoholicBewerage>> brokenBoxOfOneBeer = boxOf(Beer.class);
  static Box<? extends Class<? extends AlcoholicBewerage>> boxOfOneBeer = boxOf(Beer.class);
  static Box<Class<? extends AlcoholicBewerage>> boxOfBeerAndVodka = boxOf(Beer.class, Vodka.class);

  interface AlcoholicBewerage {}

  class Beer implements AlcoholicBewerage {}

  class Vodka implements AlcoholicBewerage {}

  static class Box<T> {}

  static <E> Box<E> boxOf(E e) {
      return new Box<E>();
  }

  static <E> Box<E> boxOf(E e1, E e2) {
      return new Box<E>();
  }
}

第一个声明brokenBoxOfOneBeer给出了编译错误:

found   : lt.tool.CompilationProblems1.Box<java.lang.Class<lt.tool.CompilationProblems1.Beer>>
required: lt.tool.CompilationProblems1.Box<java.lang.Class<? extends   lt.tool.CompilationProblems1.AlcoholicBewerage>>
static Box<Class<? extends AlcoholicBewerage>> brokenBoxOfOneBeer = boxOf(Beer.class);

此错误发生在 OpenJDK 6、Eclipse 和 IntelliJ 上。我知道这是类型推断器的限制。

在第三种情况下(boxOfBeerAndVodka),我相信编译器能够推断出正确的协变类型,因为它有两个子类型可供选择。但是为什么编译器不能编译第一个声明,而第二个声明却可以呢?

4

2 回答 2

1

但是为什么编译器不能编译第一个声明,而第二个声明却可以呢?

在这两种情况下,表达式boxOf(Beer.class)都有类型Box<Class<Beer>>

  • 第一个声明需要它有 type Box<Class<? extends AlcoholicBewerage>>; 因为Box<Class<Beer>>不是 的子类型Box<Class<? extends AlcoholicBewerage>>,所以这不起作用。(Class<Beer>是 的子类型Class<? extends AlcoholicBewerage>,但由于不变性,这并不意味着Box<Class<Beer>>是 的子类型Box<Class<? extends AlcoholicBewerage>>。)
  • 第二个声明需要它有类型Box<? extends Class<? extends AlcoholicBewerage>>——它确实如此。Class<Beer>是 的子类型Class<? extends AlcoholicBewerage>,ergoBox<Class<Beer>>是 的子类型Box<? extends Class<? extends AlcoholicBewerage>>

也就是说,您的声明中发生的事情与以下情况完全相同:

List<Object> foo = new ArrayList<String>(); // doesn't work
List<? extends Object> bar = new ArrayList<String>(); // works

它只是看起来更复杂,因为ObjectString有另一个级别的泛型类型。

于 2012-09-21T15:35:45.000 回答
1

类型参数首先根据实际参数 ( Beer.class) 推断 ( §15.12.2.7 )。如果无法根据实际参数推断类型参数,brokenBoxOfBeer则考虑上下文 ( ) ( §15.12.2.8 )。

因此,您可以按如下方式解决此问题。

static <X, E extends X> Box<X> boxOf(E e) {
  return new Box<X>();
}

编辑:我应该注意,这是一种解决方法,并带有自己的一系列问题。嵌套调用将不再被正确推断。

Box<Box<Integer>> x = boxOf(boxOf(1)); // won't compile
于 2012-09-21T15:26:28.000 回答