Java error: CAP#1, int where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of?

Java error: CAP#1, int where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of?

有了这个通用元组 class

public class Pair< T1, T2 > {

  private final T1 first;
  private final T2 second;

  public Pair(T1 first, T2 second) {
    this.first = first;
    this.second = second;
  }

  public T1 getFirst() {
    return first;
  }

  public T2 getSecond() {
    return second;
  }

}

和这个driver

public class PairDriver {

  public static void main(String[] args) {

    Pair<?, ?>[] s = new Pair<?, ?>[3];
    s[0] = new Pair<String, Integer>("a", 1);
    s[1] = new Pair<String, Integer>("b", 2);
    s[2] = new Pair<String, Integer>("c", 3);

    System.out.println( s[0].getFirst().getClass().getName() ); // Should be String
    System.out.println( s[0].getSecond().getClass().getName() ); // Should be Integer

    System.out.println( s[0].getFirst() );
    System.out.println( s[0].getFirst() + "!!!" ); // Operation works

    System.out.println( s[0].getSecond() );
    System.out.println( s[0].getSecond() + 2 ); // Operation FAILS...
  }

}

我收到以下错误:

pairDriver.java:17: error: bad operand types for binary operator '+'
    System.out.println( s[0].getSecond() + 2 ); // Operation FAILS...
                                         ^
  first type:  CAP#1
  second type: int
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error
error: compilation failed

我很困惑为什么会这样。直到标记为 // Operation FAILS... 运行 的那一行之前的所有行——类型似乎是正确的,操作甚至可以与 String 一起使用,但不能与 Integer 一起使用。

作为 follow-up:我看到有人通过用类型替换通配符 ? 来修复类似的错误。我不明白为什么这在他们的情况下有效,但在这里不适用,因为用 Pair<String, Integer>[] s = new Pair<String, Integer>[3]; 之类的类型声明数组会导致不同的错误:

pairDriver.java:5: error: generic array creation
    Pair<String, Integer>[] s = new Pair<String, Integer>[3];
                                ^
1 error
error: compilation failed

我对为什么会发生这种情况感到困惑和好奇。

I'm confused as to why this happens

就编译器而言,您正在向 int 添加一些对象。

它没有更多信息,因为该对中第二个元素的类型是 ?(如 Pair<?, ?>)。它不知道该值实际上是一个 Integer。

As a follow-up

由于类型擦除,您无法使用不可具体化的元素类型创建数组。

可具体化的类型是一种在运行时关于该类型的一切都已知的类型。非泛型是可具体化的;所有类型参数都是通配符的泛型类型是可具体化的;其他泛型类型不是。

泛型和数组是不愉快的伙伴。如果您使用泛型,请改用 List

List<Pair<String, Integer>> s = new ArrayList<>();
s.add(new Pair<String, Integer>("a", 1));
s.add(new Pair<String, Integer>("b", 2));
s.add(new Pair<String, Integer>("c", 3));

添加到您的 Pair class 中的数据类型在运行时被删除。编译器只知道来自 getFirst()getSecond() 的数据是 扩展 Object 的东西 ,但它不知道那是什么某事是具体的。

由于 Object 有一个 .toString() 方法,当您使用字符串连接运算符时会隐式调用该方法,因此 s[0].getFirst() + "!!!" 工作得很好。但是 <? extends Object>(这是编译器在您使用它时所知道的关于您的类型的所有信息)作为加法运算符的左侧无效。

您可以通过将结果转换为整数来解决这个问题,如下所示:

System.out.println( (Integer) s[0].getSecond() + 2 ); 

但请注意,这只是您告诉编译器 "trust me, this is definitely an Integer"。