为什么Java可以推断多个上限类型的共同祖先,而不是下限类型?
更具体地说,请考虑以下示例:
static class Test {
static <T> T pick(T one, T two) {
return two;
}
static void testUpperBound() {
List<? extends Integer> extendsInteger = new ArrayList<>();
// List<? extends Integer> is treated as a subclass of List<? extends Number>
List<? extends Number> extendsNumber = extendsInteger;
// List<? extends Number> is inferred as the common superclass
extendsNumber = pick(extendsInteger, extendsNumber);
}
static void testLowerBound() {
List<? super Number> superNumber = new ArrayList<>();
// List<? super Number> is treated as a subclass of List<? super Integer>
List<? super Integer> superInteger = superNumber;
// The inferred common type should be List<? super Integer>,
// but instead we get a compile error:
superInteger = pick(superNumber, superInteger);
// It only compiles with an explicit type argument:
superInteger = Test.<List<? super Integer>>pick(superNumber, superInteger);
}
}
答案 0 :(得分:2)
我想我可以解释为什么Java可以区分下限和上限类型。
当使用不兼容的边界时,尝试推断公共下限可能会失败,例如Integer
和Long
。当我们使用上限时,总是可以找到一些共同的上限,在这种情况下List<? extends Number>
。但List<? super Integer>
和List<? super Long>
没有共同的下限。如果发生此类冲突,唯一安全的选择是返回List<? extends Object>
,与List<?>
同义,意思是“List
未知类型”。
现在,可以说,只有在实际存在冲突边界的情况下,我才能采取这种做法,而不是我的问题中的情况。但也许决定采取简单的方法,除非明确指定,否则不要假设有一个共同的下限。
答案 1 :(得分:0)
我使用1.8.0_25并且我收到了编译错误。 但是,错误并不是对pick的调用很糟糕,而是要将结果放入的变量。 重复你的例子:
static void testLowerBound() {
List<? super Number> superNumber = new ArrayList<>();
List<? super Integer> superInteger = superNumber;
// this gets the error
superInteger = pick(superNumber, superInteger);
// this doesn't
pick(superNumber, superInteger);
// what's happening behind is
List<? extends Object> behind = pick(superNumber, superInteger);
superInteger = behind;
// that last line gets the same compilation error
}
如果你看看在调用中如何替换T,参数将被用作List,丢失有关下限的信息。
关于推论:每一个?不完全是&#34;无论可以分配给什么......&#34;但是&#34;我想要命名的特定类型,可以分配给...&#34;。这很重要,因为在你的例子中,你得到3个变量,每个列表1个,另一个,不同的,变量的结果。 现在,由于pick的声明,T的替换必须满足参数的类层次结构。在第一种情况下,您需要替换&lt;#1 extends Integer&gt;和&lt;#2扩展数字&gt;。 #2可能是Double,所以你得到的最好线索是#3扩展了Number。 在第二种情况下,您需要替换&lt;#1 super Integer&gt;和&lt;#2 super Number&gt;。现在这意味着#2可以是Number,Object,Serializable中的任何一个; #1添加到该列表Comparable和Integer。组合可以是Number,Object(和T应该是Object);或者Serializable,Integer(和T可以是Serializable),所以最好的线索是T是未知类型扩展Object的List。
当然它只能到达Number,但你不能为同一个类型变量获得两个边界,所以必须让它在那个