Static Generic Methods, Type Inference causes java.lang.VerifyError: Verifier rejected class

Static Generic Methods, Type Inference causes java.lang.VerifyError: Verifier rejected class

考虑这些经典的 Person 多态层次结构...

超级接口

public interface Person {

    String getName();

    interface Builder<P, B extends Builder<P, B>> {
        B name(String name);
        P build();
    }
}

Student.java

public class Student implements Person {

    @Override
    public String getName() {
        ...
    }

    public static class Builder implements Person.Builder<Student, Builder> {

        @Override
        public Builder name(String name) {
            ...
        }

        @Override
        public Student build() {
            ...
        }
    }
}

Employee.java 我在这里省略了Employee类型,因为它和student一样

PersonBuilderFactory.java

public class PersonBuilderFactory {

public static <T extends Person.Builder> T getBuilder(T...args) {

    if (args.getClass().getComponentType().isAssignableFrom(Student.Builder.class)) {
        return (T) new Student.Builder();
    } else if (args.getClass().getComponentType().isAssignableFrom(Employee.Builder.class)) {
        return (T) new Employee.Builder();
    }

    throw new RuntimeException("No such builder for other person types");
}

TestInference.java

public class TestInference {

public static void testInference() {

    Compile Test 1
    // java wants me to infer the type argument
    // which will build perfectly fine
    Student.Builder studentBuilder1 = PersonBuilderFactory.<Student.Builder>getBuilder();
    Employee.Builder employeeBuilder1 = PersonBuilderFactory.<Employee.Builder>getBuilder();

    Compile Test 2
    // so if i remove those explicit type arguments
    // everything still works fine
    Student.Builder studentBuilder2 = PersonBuilderFactory.getBuilder(); // this will implicitly  get a student builder
    Employee.Builder employeeBuilder2 = PersonBuilderFactory.getBuilder(); // this will have an employee builder

    Compile Test 3
    // Now...
    // leaving the argument type NOT inferred and changing the data type
    // gives me a compile error which is Im expecting and I want
    String someString = PersonBuilderFactory.<Student.Builder>getBuilder();

    Compile Test 4
    // but when i remove the explicit type argument
    // it compiles but this will cause me a specific Verify Error
    String someAnotherString = PersonBuilderFactory.getBuilder();
}

}

编译测试 1 有明确的类型参数,java 要求我删除(告诉我它有点冗余)导致编译测试 2,效果很好

编译测试 3 是我所期望的 if

编译测试 4 让我感到困惑,为什么编译器没有说它返回的东西没有 Person.Builder (T extends Person.Builder),当我 运行 代码时,它抛出一个验证错误告诉我我有一个错误的方法,我的问题是为什么编译器没有?但是当我明确指定一个类型参数时它确实如此,当我声明一个正确的变量数据类型时,它 returns 一个正确的类型。

我在这里很难理解类型推断,有问题说它与 Java 编译器版本有关,另一个 post 说 "you just have a very complex method"

如有任何帮助,我们将不胜感激。

when I ran the code, it throws me a Verify Error that tells me I have a bad method, my question is why the compiler didnt?

因为编译器不能确定你做错了什么。

问题是 Builder 是一个接口;请注意,如果将 Builder 变成 class,则代码根本无法编译。关于 String 是最终的 class 这一事实,类型推断不会以特殊的方式对待它(Chapter 18 of JLS 只在单词 "finally" 中提到 "final" ; 它根本没有提到 final)。

所以它允许案例 4,因为 class 比如:

class Something extends String implements Person.Builder {}
就类型推断而言,

可能的。类似地,<T extends String> 是允许的,即使没有扩展 String.

该行的推断类型是交集类型:

INT#1 extends String,Builder

(我通过 -Xlint:unchecked' 编译得到这个;方便的是,那里有一个关于未经检查的通用数组创建的警告)

因此编译器似乎允许它。

但是,在方法中,args.getClass().getComponentType()的值是java.lang.String;这与您的任何条件都不匹配,因此它在底部遇到异常。