为什么编译器在用不相关的接口类型调用时选择这个带有 class 类型参数的泛型方法?

Why does the compiler choose this generic method with a class type parameter when invoked with an unrelated interface type?

考虑以下两个类和接口:

public class Class1 {}
public class Class2 {}
public interface Interface1 {}

如果getInterface1Interface1Class2没有关系,为什么第二次调用mandatory会调用Class2的重载方法?

public class Test {

    public static void main(String[] args) {
        Class1 class1 = getClass1();
        Interface1 interface1 = getInterface1();

        mandatory(getClass1());     // prints "T is not class2"
        mandatory(getInterface1()); // prints "T is class2"
        mandatory(class1);          // prints "T is not class2"
        mandatory(interface1);      // prints "T is not class2"
    }

    public static <T> void mandatory(T o) {
        System.out.println("T is not class2");
    }

    public static <T extends Class2> void mandatory(T o) {
        System.out.println("T is class2");
    }

    public static <T extends Class1> T getClass1() {
        return null;
    }

    public static <T extends Interface1> T getInterface1() {
        return null;
    }
}

我理解 Java 8 和 Java 7:

$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac -source 1.7 -target 1.7 *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning
T is not class2
T is not class2
T is not class2
T is not class2

并且 Java 8(也用 11 和 13 测试):

$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test                        
T is not class2
T is class2
T is not class2
T is not class2

类型推断规则在 Java 8 中进行了重大修改,最显着的目标类型推断得到了很大改进。因此,在 Java 8 之前,方法参数站点没有收到任何推断,默认为擦除类型(Class1 代表 getClass1()Interface1 代表 getInterface1()),在 Java 8 中推断出最具体的适用类型。 Java 8 的 JLS 引入了 Chapter 18. Type Inference 的新章节,Java 7.

的 JLS 中缺少该章节

<T extends Interface1>最具体的适用类型是<X extends RequiredClass & BottomInterface>,其中RequiredClass是上下文需要的class,BottomInterface是底所有接口的类型(包括 Interface1)。

注意:每个 Java 类型都可以表示为 SomeClass & SomeInterfaces。因为 RequiredClassSomeClass 的子类型,而 BottomInterfaceSomeInterfaces 的子类型,所以 X 是每个 Java 的子类型类型。因此,X是Java底型。

X 匹配 public static <T> void mandatory(T o)public static <T extends Class2> void mandatory(T o) 方法签名,因为 X 是 Java 底部类型。

因此,根据 §15.12.2mandatory(getInterface1()) 调用了 mandatory() 方法最具体的重载,即 public static <T extends Class2> void mandatory(T o) 因为 <T extends Class2><T>.

下面是如何显式指定 getInterface1() 类型参数以使其 return 匹配 public static <T extends Class2> void mandatory(T o) 方法签名的结果:

public static <T extends Class2 & Interface1> void helper() {
    mandatory(Test.<T>getInterface1()); // prints "T is class2"
}

<T extends Class1>最具体的适用类型是<Y extends Class1 & BottomInterface>,其中BottomInterface是所有接口的底层类型。

Y 匹配 public static <T> void mandatory(T o) 方法签名,但不匹配 public static <T extends Class2> void mandatory(T o) 方法签名,因为 Y 没有扩展 Class2

因此 mandatory(getClass1()) 调用 public static <T> void mandatory(T o) 方法。

getInterface1() 不同,您不能显式指定 getClass1() 类型参数以使其 return 匹配 public static <T extends Class2> void mandatory(T o) 方法签名的结果:

                       java: interface expected here
                                     ↓
public static <T extends Class1 & C̲l̲a̲s̲s̲2> void helper() {
    mandatory(Test.<T>getClass1());
}