静态通用方法,类型推断导致java.lang.VerifyError:Verifier拒绝了类

时间:2017-06-19 10:29:12

标签: java generics polymorphism static-methods type-inference

考虑这些经典的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类型,因为它与学生

一样

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 是我期待的

  • 我声明了一个显式类型参数,它不在变量
  • 的声明类型的范围内

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

我完全很难理解Type Inference,有些问题表明它与Java Compiler Version有关,另一篇帖子说&#34;你只是有一个非常复杂的方法&#34;

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

  

当我运行代码时,它会抛出一个验证错误,告诉我我的方法不好,我的问题是为什么编译器没有?

因为编译器无法确定您做错了什么。

问题是Builder是一个接口;请注意,如果将Builder放入类中,则代码根本不会编译。类型推断不会以特殊方式处理String,因为它是最终类(Chapter 18 of JLS仅提及&#34;最终&#34;在单词& #34;最后&#34 ;;它根本没有提到final

所以它允许案例4,因为类似于:

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

可能的。同样,即使没有任何内容延伸<T extends String>,也允许使用String

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

INT#1 extends String,Builder

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

因此似乎是编译器允许的。

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