如何返回类型返回类型也是方法参数的上限和下限

时间:2015-09-01 16:53:21

标签: java generics inheritance type-inference

假设我们有以下代码:

class A {}

class B extends A {}

class C extends B {}

public static <T> T testMe(List<? super T> list1,List<? extends T> list2) {
    return null;
}

public static void main(String[] args) {
    List<B> listB = new ArrayList<>();
    List<C> listC = new ArrayList<>();
    // All three variants are possible:
    A a=testMe(listB, listC);
    B b=testMe(listB, listC);
    C c=testMe(listB, listC);
}

问题是关于public static <T> T testMe(List<? super T> list1,List<? extends T> list2)。如果有三个类,编译器如何确定T类型:A,B,C,?当我分析Collections.copy时,就出现了这个问题。

4 个答案:

答案 0 :(得分:4)

编译器在所有3种情况下都推断出类型参数C的类型T

适合约束的是most specific type

  

[T]他的推理算法试图找到适用于所有参数的最具体的类型。

对于前两个陈述,

A a = testMe(listB, listC);
B b = testMe(listB, listC);

BC都匹配,因为List<B>匹配List<? super B>List<C>匹配List<? extends B>List<B>匹配List<? super C>List<C>匹配List<? extends C>。编译器选择匹配的最具体类型C

您可以使用显式类型参数进行编译,以使编译器将其解析为B

A a = Super.<B>testMe(listB, listC);
B b = Super.<B>testMe(listB, listC);

在第三行中,只有C匹配,因此这是编译器为T选择的内容。

C c = testMe(listB, listC);

这是因为分配的变量属于C类型,而B无法分配给C

答案 1 :(得分:1)

如果您将签名更改为需要T的令牌,很快就会发现正在发生的事情:

public static <T> T testMe(Class<T> c, List<? super T> list1, List<? extends T> list2) {
    return null;
}

public static void main(String[] args) {
    List<B> listB = new ArrayList<>();
    List<C> listC = new ArrayList<>();
    A a = testMe(A.class, listB, listC); // compile error
    B b = testMe(B.class, listB, listC); // OK. T == B
    C c = testMe(C.class, listB, listC); // OK. T == C
}

在您的示例中编译的原因:

A a = testMe(listB, listC);

是因为T被推断为B(或C - 无关紧要),但B也是A的实例,因此,可以将类B的对象分配给A类型的变量。

答案 2 :(得分:0)

从纯理论的角度来看,为什么类型论证是什么重要?该类型未编码到编译的字节码中。它唯一的相关性是编译时类型检查是否通过。为此,如果编译器能够保证某些选择类型参数使得代码编译,那么哪一个以及是否有更多应该是不相关的。在前两种情况中,存在多种类型(BC);编译器无需决定一个。在第三种情况下,只有一种类型(C)有效。在所有情况下,如果编译器认为至少有一种类型有效,那么它应该让它编译。<​​/ p>

答案 3 :(得分:-1)

编译器在编译时知道您传递的Object的类型是什么,例如List<B>,因为泛型是编译时功能。它可以使用此信息来检查类型错误。在编译之后,由于类型擦除而遗漏了泛型,代码实际上就是这样:

 public static Object testMe(List<Object> list1, List<Object> list2) {
    //some code
    return null;
}