2017-06-19 31 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