2015-02-10 57 views
7

「不能從類型變量選擇」 我有以下類別:爲什麼

public abstract class A { 

    public String att; 

    public static abstract class Builder<T extends A> { 

     public T a; 

     public abstract T build(); 

     public T.Builder setAtt(String a) { 
      this.a.att = a; 
      return this; 
     } 
    } 
} 


public class A1 extends A { 

    public static class Builder extends A.Builder<A1> { 

     public Builder() { 
      this.a = new A1(); 
     } 

     public A1 build() { 
      return this.a; 
     } 
    } 
} 


public class A2 extends A { 

    public String subAtt; 

    public static class Builder extends A.Builder<A2> { 

     public Builder() { 
      this.a = new A2(); 
     } 

     public A2 build() { 
      return this.a; 
     } 

     public Builder setSubAtt(String subAtt) { 
      a.subAtt = subAtt; 
      return this; 
     } 
    } 
} 

爲什麼我會收到「不能選擇從類型變量「出錯A.setAtt

類型刪除不適用。 TA1A2,但這在編譯時已知。

那麼我該如何返回子類構建器呢?我的主要目標是能夠在setter混合子類和超類之後做setter。

+4

你能分享一下觸發錯誤的代碼嗎?將代碼粘貼到IDE中不會觸發編譯錯誤。 – dasblinkenlight 2015-02-10 14:28:55

+0

類型擦除確實適用,因爲人們可以輕鬆地創建另一個類,從外部擴展A,並且系統會中斷。 – Clashsoft 2015-02-10 14:29:30

+0

那麼有沒有辦法做到這一點? – Gust 2015-02-10 14:51:18

回答

3

正如Marko解釋的那樣,您不能簡單地使用'T.Builder'並期望編譯器確定未知類型的嵌套類。

你可以做的是給力的Builder一個子類,證明自己的身份:

public class A 
{ 

    public String att;  

    public static abstract class Builder<T extends A, U extends Builder<T, U>> 
    { 

     public T a; 

     public abstract T build(); 

     public U setAtt(String a) 
     { 
      this.a.att = a; 
      return getBuilder(); 
     } 

     public abstract U getBuilder();   


    }   
} 


public class A1 extends A 
{ 

    public static class Builder extends A.Builder<A1, A1.Builder> 
    { 

     public Builder() 
     { 
      this.a = new A1(); 
     } 

     @Override 
     public A1 build() 
     { 
      return this.a; 
     } 


     @Override 
     public A1.Builder getBuilder() 
     { 
      return this; 
     } 
    } 
} 

這樣像

A1 build = new A1.Builder().setAtt("x").build(); 

語句會工作。

+0

在抽象的'Builder'類中的字段'a'的附加說明:爲了避免在設置超類'attribut時設置NPE,你可以考慮通過getter獲得'a':public abstract T getA()'。 – flo 2015-02-10 15:34:34

+0

'U'上的邊界是不必要的:'class Builder '也可以工作 – newacct 2015-02-10 23:41:40

+0

當你想要使用超類型的方法'setAtt'時,'U' (s)在構建過程的任何階段,而不僅僅是作爲第一次調用。 – flo 2015-02-11 06:08:05

5

這不可能工作:

T.Builder 

由於T是一種變量,因此不綁定到任何特定的類型,你不能指望編譯器來解決嵌套類型的類型未知。

T要麼A1A2但這在編譯時已知的。

這種假設是錯誤的:假設您將代碼作爲JAR提供,另一位開發人員使用它,並引入了他自己的子類A。如果Java代碼是在封閉世界的假設下編譯的,那麼Maven將是最無用的服務。

該假設也是無關緊要的:您需要一個相當複雜的類型系統來確定一般類型T.Builder是否符合。