2017-06-19 78 views
1

考虑这些经典的人多态层次...静态泛型方法,类型推断导致java.lang.VerifyError的:验证拒绝类

超接口

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 我这里省略了员工类型,它只是同与学生

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(); 
} 

}

Co mpile测试具有显式类型参数的Java要求我删除(告诉我,它是某种redudant)导致编译测试,工作正常

编译测试是林预计,如果

  • 我宣布一个明确的类型参数是不声明类型变量

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

林完全有一个很难理解这里的类型推断,有问题,说有事情做与Java编译器版本,以及另一篇文章中说:“你只是有一个非常复杂的方法”

任何帮助不胜感激。

回答

1

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编入类中,则代码根本不会编译。类型推断不会以特殊的方式对待String,因为它是最终类(Chapter 18 of JLS在“finally”中只提到“final”;它根本没有提及final)。

所以它允许情况下,4,因为喜欢的一类:

class Something extends String implements Person.Builder {} 

可能,尽量类型推断关注。同样,<T extends String>是允许的,即使没有任何延伸String

在该行上的推断类型是交叉点型:

INT#1 extends String,Builder 

(I通过用-Xlint:unchecked编译得到这个“;方便地,有一个关于未检查的通用阵列创建有警告)

因此它似乎被编译器所允许。

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

+0

是的!非常感谢在基础知识上提醒我,我忘记了接口和类(absctract或具体)之间的关系,任何类都可以实现MANY接口,但一个类只能扩展一个父类! – Robert

+0

我只是不明白交叉口类型:p – Robert

+0

现在我即将获得所需的编译时错误! :),“类型参数T具有不兼容的上限” – Robert