为什么编译器不推断会捕获泛型中的关系?

时间:2015-07-16 19:23:31

标签: java generics compiler-errors

想象一下像这样的界面

public interface MessageParameter<T> {

  public List<T> unmarshal(byte[] array);
  public int getLength(List<T> values);

}

和该界面的消费者

public class GenericUser {

  List<MessageParameter<?>> payload = new ArrayList<>();

  public void run() {
     byte[] byteArray = new byte[] { 1, 2 };

     for (MessageParameter<?> element : payload) {
        element.getLength(element.unmarshal(byteArray)); //compiler error
     }
  }
}

编译器发出错误

 The method getLength(List<capture#1-of ?>) in the type MessageParameter<capture#1-of ?> is not applicable for the arguments (List<capture#2-of ?>)

显然,由于我在两个方法调用中都使用element,因此两者的类型相同且应该被允许。问同一个问题的另一种方法,为什么编译器会创建capture#2?为什么不能推断它们在逻辑上都是相同的capture

我错过了什么吗?是否有一个反例,此代码会引发运行时异常?

我的主要问题不是如何修复代码(虽然这也很有趣,我目前的解决方案是使用Object代替?),但逻辑是什么这个错误的原因是什么?它看起来像编译器的实现上的缺点而不是逻辑限制

2 个答案:

答案 0 :(得分:4)

答案是编译器并不聪明地接受与?对应的运行时类型是相同的,因为它不关心你的单行表达式是否涉及相同的element:< / p>

element.getLength(element.unmarshal(byteArray));

在语义上类似于:

List<?> unmarshalledList = element.unmarshal(byteArray);
element.getLength(unmarshalledList);

在这种情况下,列表unmarshalledList肯定必须具有相同的&#34;任何类型&#34;正如getLength()所期望的那样。以上是两个单独的陈述(即使它们是连续的)。想象一下,他们并不是连续的。你可能有类似的东西:

MessageParameter<?> otherElement = getOtherElement();
for (MessageParameter<?> element : payload) {
    List<?> unmarshalledList = element.unmarshal(byteArray);
    // unmarshalledList can be re-assigned from another parameterized type
    unmarshalledList = otherElement.unmarshal(byteArray);
    element.getLength(unmarshalledList);  // error
}

换句话说,当程序到达调用unmarshalledList的语句时,编译器不能假定变量?将保留elementgetLength的相同v0.13.1类型。 em>相同的元素。它可以重新分配给其间的不同参数化类型。

答案 1 :(得分:0)

我相信你误解了?在通用中的含义。该符号称为通配符;它指的是一种真正未知的类型。这与您当前的努力不同,在这种努力中,使用Object会更好,因为您的类型并非完全未知 - 您知道它们都实现Object并且可以像这样引用它们。 (? extends Object在某些地方可能会更好。)

至于?Object不同义的原因,请记住原语不会从Object继承,但可以使用通配符引用。因此,在类型擦除之后,您的程序无法确定这两个通配符是指兼容实体;除非你明确告诉它。