想象一下像这样的界面
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
代替?
),但逻辑是什么这个错误的原因是什么?它看起来像编译器的实现上的缺点而不是逻辑限制
答案 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
的语句时,编译器不能假定变量?
将保留element
类getLength
的相同v0.13.1
类型。 em>相同的元素。它可以重新分配给其间的不同参数化类型。
答案 1 :(得分:0)
我相信你误解了?
在通用中的含义。该符号称为通配符;它指的是一种真正未知的类型。这与您当前的努力不同,在这种努力中,使用Object
会更好,因为您的类型并非完全未知 - 您知道它们都实现Object
并且可以像这样引用它们。 (? extends Object
在某些地方可能会更好。)
至于?
与Object
不同义的原因,请记住原语不会从Object
继承,但可以使用通配符引用。因此,在类型擦除之后,您的程序无法确定这两个通配符是指兼容实体;除非你明确告诉它。