2017-03-23 17 views
1

継承を使用してBuilderパターンを実装したいと思います。だから私は4つのクラスがあります:1つの抽象クラス(ClassA)、ClassB、ClassC。 TESTTESTクラスは、すべてこれがどのように動作するかを確認するために使用されます。Java:ビルダーパターン、継承、およびジェネリック

public abstract class ClassA { 

    private String aString; 

    public String getaString() { 
     return aString; 
    } 

    public abstract class ClassABuilder<T extends ClassABuilder>{ 

     public T setaString(String str) { 
      ClassA.this.aString = str; 
      return (T)this; 
     } 

     public abstract ClassA build(); 

    } 
} 

public class ClassB extends ClassA{ 

    private String bString; 

    public String getbString() { 
     return bString; 
    } 

    public class ClassBBuilder<T extends ClassBBuilder> extends ClassA.ClassABuilder<T>{ 

     public T setbString(String str) { 
      ClassB.this.bString = str; 
      return (T)this; 
     } 

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

public class ClassC extends ClassB{ 

    private String cString; 

    public String getcString() { 
     return cString; 
    } 

    public static ClassCBuilder<ClassCBuilder> newBuilder(){ 
     return new ClassC().new ClassCBuilder(); 
    } 

    public class ClassCBuilder<T extends ClassCBuilder> extends ClassB.ClassBBuilder<T>{ 

     public T setcString(String str) { 
      ClassC.this.cString = str; 
      return (T)this; 
     } 

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

public class TestTest { 

    public static void main(String[] args) { 
     // TODO code application logic here 
     ClassC C=ClassC.newBuilder() 
       .setaString(null) 
       .setbString(null) 
       .setcString(null) //LINE XXX 
       .build(); 
    } 
} 

問題がTESTTESTでLINE XXXで、私はシンボル「setcString」を見つけることができない得るということです。何が間違っていますか?

+0

ビルダーは静的である必要があります。そうでない場合は、ビルドしようとしているクラスのインスタンスを作成するために、ビルダーを作成するためにビルドするクラスのインスタンスが必要です。 –

+0

@Andy Turner私はClassBにビルドメソッドを追加しませんでしたが、classCを参照してください - そのnewBuilderメソッドは静的です。 –

+2

'ClassABuilder 'は 'ClassABuilder >'でなければなりません。他のビルダーと同じです。 –

答えて

1

の階層に沿ってそれを追跡してみましょう:

まず、この署名を考えてみます。

class ClassABuilder<T extends ClassABuilder> 

あなたは返さTClassABuilderを拡張するオブジェクトになりますsetaString(null)を呼び出し

。コンパイラはこれが ClassBBuilderであることを認識しており、 setbString(null)に電話することができます。定義状態 TClassBBuilderの一般的なタイプの ClassBBuilderだけの任意の情報を拡張するために必要とされているので

しかし、失われます。したがって、コンパイラはTClassBBuilderであることを知っていますが、実際にはClassCBuilderClassBBuilder<ClassCBuilder>に拡張されているため、返される型はsetcString()ではありません。

すでに言及したように、T extends ClassABuilder<T>を使用すると、コンパイラは階層に渡される別の汎用タイプがあることを知っているので、これを修正します。

newBuilder()は、このように見えなければならないでしょう:

@AndyTurnerがすでに観察されたように
public static ClassCBuilder<?> newBuilder(){ 
    //you have too create a raw type here so you'll have to ignore/suppress/live with the warning 
    return (new ClassC().new ClassCBuilder()); 
} 
1

、問題は型パラメータとしてあなたのビルダークラス型の生のバージョンを使用することです。彼は詳細には触れませんでしたが、結論はこれです:

ClassC C=ClassC.newBuilder() // yields a ClassCBuilder<ClassCBuilder> 
     .setaString(null) // yields a raw ClassCBuilder (type parameter) 
     .setbString(null) // yields a raw ClassBBuilder (type parameter bound) 
     .setcString(null) // ERROR: no such method on ClassBBuilder 
     .build(); 

あなたのクラス構造と戦略に対する最小限の変更でこれを修正するにはアンディは助言として、あなただけの、あなたのビルダークラスの型パラメータの境界を修正してはなりません。...

ClassABuilder<T extends ClassABuilder<T>> 

... などが、だけでなく、それがジェネリックにするなど、ClassC.newBuilder()に変更を加える:

public static <T extends ClassCBuilder<T>> ClassCBuilder<T> newBuilder() { 
     return new ClassC().new ClassCBuilder<T>(); 
    } 

変更の組み合わせにより、あなたのコードが私のためにコンパイルされます。

関連する問題