通用行为

时间:2014-05-29 14:11:58

标签: java generics

(对不起双关语)

假设有人想要定义一个通用构建器,如下所示:

public abstract class GenericBuilder<T extends Product> {
    int x;
    int y;

    <K extends GenericBuilder<T>> K setX(int x) {
        this.x = x;
        return (K)this;
    }

    <K extends GenericBuilder<T>> K  setY(int y) {
        this.y = y;
        return (K) this;
    }

    abstract T build();
}

abstract class Product {
    int x;
    int y;
}

class ConcreteProduct extends Product {
    int z;
}

class ConcreteBuilder extends GenericBuilder<ConcreteProduct>{
    int z;

    <K extends GenericBuilder<ConcreteProduct>> K  setZ(int z) {
        this.z = z;
        return (K) this;
    }

    @Override
    ConcreteProduct build() {
        ConcreteProduct cp = new ConcreteProduct();
        cp.x = x;
        cp.y = y;
        cp.z = z;
        return cp;
    }

    public static void main(String[] args) {
        new ConcreteBuilder().setX(1).setY(2).setZ(3);
    }
}

调用ConcreteBuilder.setZ()时,它在编译期间失败。

为什么?是否应该擦除?或者,仿制药不包含有关其通用参数的信息?

修改

任何想法如何避免在第二个通用参数中使用:

public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder>

即。 &lt; ...,ConcreteBuilder&gt ;,这似乎有点笨拙?我想这不可能。是否有其他语言(C#可能?)允许这样做?

2 个答案:

答案 0 :(得分:2)

以这种方式破解您的代码,您将了解您的班级GenericBuilder<ConcreteProduct>没有定义任何setZ()方法。

    GenericBuilder<ConcreteProduct> setY = new ConcreteBuilder().setX(1).setY(2);
    setY.setZ(3);

答案 1 :(得分:2)

GenericBuilder您的函数中,如果您未指定函数的类型参数,则返回GenericBuilder。在您的主要功能中,对setX的调用会返回GenericBuilder,并且您将丢失实际使用ConcreteBuilder的信息。要成功进行调用,您必须为setter指定通用参数:

new ConcreteBuilder().<ConcreteBuilder>setX(1).<ConcreteBuilder>setY(2).setZ(3);

替代

您可以向GenericBuilder添加第二个类型参数:

public abstract class GenericBuilder<T extends Product, K extends GenericBuilder<T, K>> {
    int x;
    int y;

    K setX(int x) {
        this.x = x;
        return (K)this;
    }

    K  setY(int y) {
        this.y = y;
        return (K) this;
    }

    abstract T build();
}

并将ConcreteBuilder更改为:

public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder> {
    int z;

    ConcreteBuilder setZ(int z) {
        this.z = z;
        return this;
    }

    @Override
    public ConcreteProduct build() {
        ConcreteProduct cp = new ConcreteProduct();
        cp.x = x;
        cp.y = y;
        cp.z = z;
        return cp;
    }

    public static void main(String[] args) {
        new ConcreteBuilder().setX(1).setY(2).setZ(3);
    }
}