我想通过继承实现构建器模式。因此,我有以下4类:一个抽象类(ClassA),ClassB,ClassC。 TestTest类用于查看所有工作原理:

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


问题是,在第XXX行的TestTest中,我找不到符号“ setcString”。我做错了什么?

最佳答案

让我们沿着层次结构进行跟踪:

首先考虑以下签名:

class ClassABuilder<T extends ClassABuilder>


调用setaString(null)时,返回的T将是扩展ClassABuilder的对象。编译器知道这是一个ClassBBuilder,因此将允许您调用setbString(null)

但是,由于定义状态T是扩展原始ClassBBuilder所必需的,因此只会丢失有关ClassBBuilder泛型类型的所有信息。因此,编译器仅知道TClassBBuilder,而不是实际上是扩展ClassCBuilderClassBBuilder<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());
}

10-08 14:26