2017-03-23 28 views
1

我想用继承来实现构建器模式。所以我有4个以下的类:一个抽象类(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> 

当你调用setaString(null)返回T将扩展ClassABuilder的对象。编译器知道这是一个ClassBBuilder,因此可以让您拨打setbString(null)

然而,由于定义中指出T需要延长ClassBBuilder的泛型类型一个原料ClassBBuilder唯一的任何信息都将丢失。因此,编译器只知道TClassBBuilder但不是它实际上是一个ClassCBuilder延伸ClassBBuilder<ClassCBuilder>,因此不知道返回的类型有关setcString()

正如已经提及的,使用T extends ClassABuilder<T>将解决这个问题,因为现在的编译器知道有被流传下来的架构中的另一个泛型类型。然后

newBuilder()必须是这样的:

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

由于@AndyTurner已经观察到的,问题是,你使用类型参数的生成器类类型的原始版本。他没有细说,但结果是这样的:

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

有了这些变化的组合,你的代码为我编译。