流畅的setter在java中继承

Ore*_*ren 17 java

我最近看到了一些利用这种语法的代码(亚马逊hadoop代码)

Foo bar = new Foo().setX(10).setY(11);
Run Code Online (Sandbox Code Playgroud)

我觉得那很好,所以我决定试一试.使我的setX()类型函数返回Foo而不是void放入return this;所有这些函数.这很好用.直到我尝试继承,这产生了一些结果.

我将给出一个具体的例子:我有两个类,Location有两个字段的类,x和y.和另一个Location3D继承自Location第三个字段的类,z.所有字段都使用上述方法作为其setter.

现在我想创建一个新的location3D实例并设置其字段,会发生什么

new Location3D().setZ(7).setY(6).setX(5)
Run Code Online (Sandbox Code Playgroud)

同时工作

new Location3D().setX(7).setY(6).setZ(5)
Run Code Online (Sandbox Code Playgroud)

没有.

by不起作用我的意思是返回的setY(6)是一个Location对象而不是一个location3D对象,因此没有setZ()方法!

在这个长篇介绍之后,我的问题是:这种形式的"setter stringing"可以使用继承而不强制调用者转换对象吗?如果是这样的话?

另外我确定这个术语比"setter stringing"更好,这是什么?

Man*_*qui 20

正如你所指出的那样,new Location3D().setX(7).setY(6).setZ(5)不起作用的原因是因为setX()setY()返回实例Location而不是Location3D.

您可以通过在Location类中添加泛型类型参数来使用泛型(尽管解决方案不是特别漂亮)来解决这个问题:

public class Location<T extends Location<?>> {
    protected int x, y;

    @SuppressWarnings("unchecked")
    public T setX(int x) {
        this.x = x;
        return (T) this;
    }

    @SuppressWarnings("unchecked")
    public T setY(int y) {
        this.y = y;
        return (T) this;
    }
}
Run Code Online (Sandbox Code Playgroud)

Location3D然后,您的子类将自己设置为泛型类型参数,以便超类返回实例Location3D而不是Location:

public class Location3D extends Location<Location3D> {
    protected int z;

    public Location3D setZ(int z) {
        this.z = z;
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法避免超类产生的警告,因此也就是@SuppressWarnings("unchecked")注释.

值得注意的是,如果你定义你的子类使得泛型类型参数是一个不同的类类型,那么你可以结束ClassCastException,因此你应该在你的超类中记录那些可能想要创建自己的子类的人的限制.

最后,以您描述的方式将方法调用链接在一起通常称为方法链接.您描述的setter方法的样式与构建器模式密切相关.


Joh*_*sen 9

问题是Location返回Location,因为Location3D将返回Location3D.要解决此问题,请覆盖要在Location3D中使用的方法并更改返回类型:

public class Location {
    private int x;
    private int y;

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

    public Location setX(int x){
        this.x = x;
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

和Location3D:

public class Location3D extends Location {

    private int z;

    public Location3D setY(int y){
        super.setY(y);
        return this;
    }

    public Location3D setX(int x){
        super.setX(x);
        return this;
    }

    public Location3D setZ(int z){
        this.z = z;
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

组成方法:

public class Location {
    private int x;
    private int y;

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

    public Location setX(int x){
        this.x = x;
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

和Location3D:

public class Location3D {

    private Location location = new Location();
    private int z;

    public Location3D setY(int y){
        location.setY(y);
        return this;
    }

    public Location3D setX(int x){
        location.setX(x);
        return this;
    }

    public Location3D setZ(int z){
        this.z = z;
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 那就是我会做的。但是,我确实更喜欢其他解决方案(特别是如果有许多设置方法和类要覆盖的话)。 (2认同)