java中具有继承性的Fluent setter

java中具有继承性的Fluent setter,java,Java,我最近看到一些代码(amazon hadoop代码)使用这种语法 Foo bar = new Foo().setX(10).setY(11); 我觉得这很甜蜜,所以我决定试一试。使我的setX()类型函数返回Foo而不是void,并将返回此值在所有这些中。这很有效。直到我尝试了继承,这产生了一些错误的结果 我将给出一个具体的例子:我有两个类,Locationclass,它有两个字段,x和y。和另一个类Location3D,该类继承自Location,并添加第三个字段z。 所有字段都使用上述方法

我最近看到一些代码(amazon hadoop代码)使用这种语法

Foo bar = new Foo().setX(10).setY(11);
我觉得这很甜蜜,所以我决定试一试。使我的
setX()
类型函数返回
Foo
而不是
void
,并将
返回此值在所有这些中。这很有效。直到我尝试了继承,这产生了一些错误的结果

我将给出一个具体的例子:我有两个类,
Location
class,它有两个字段,x和y。和另一个类
Location3D
,该类继承自
Location
,并添加第三个字段z。 所有字段都使用上述方法进行设置

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

new Location3D().setZ(7).setY(6).setX(5)
工作时

new Location3D().setX(7).setY(6).setZ(5)
没有

所谓不工作,我的意思是从
setY(6)
返回的是
Location
对象,而不是
location3D
对象,因此没有
setZ()
方法

在这篇冗长的介绍之后,我的问题是:这种形式的“setter string”是否可以在不强制调用方强制转换对象的情况下与继承一起使用?如果是,怎么做


另外,我确信有一个比“setter string”更好的术语,它是什么?

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

通过向
位置
类中添加泛型类型参数,可以使用泛型解决此问题(尽管解决方案并不特别漂亮):

public class Location<T extends Location<T>> {
    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;
    }
}
不幸的是,据我所知,无法避免超类产生的警告,因此出现了
@SuppressWarnings(“unchecked”)
注释

还值得注意的是,如果您定义子类,使泛型类型参数是不同的类类型,那么您可能会得到
ClassCastException
,因此您应该在您的超类中为任何想要创建自己的子类的人记录该限制


最后,以您描述的方式将方法调用链接在一起通常称为。您所描述的setter方法的样式与位置密切相关。

问题在于位置返回位置,因为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;
    }
}
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;
    }
}
和位置3D:

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;
    }
}
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;
    }
}
组合方法:

和位置3D:

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

仅供参考,此“串接”通常称为“串接”。请发布
setX
等方法的代码。特别是对于
setY
。即使在继承的方法(在子类中未重写)中,返回
this
也应返回对象本身,这不会因为调用了基类中的方法而更改类。您可以重写所有子类中的setter。恐怕这就是你必须要做的。公共位置setX(intx){this.x=x;返回这个;}‘Fluent API’或‘Fluent Interface’是你正在寻找的术语,用来描述你正在做的事情。非常好。我只想重写setter。这个想法还可以,但是这段代码有几个编译错误。稍微修正一下:它应该是
公共类位置
。否则,第二个
位置
是一个原始表单(它不适合泛型)。然后
Location3D扩展了Location
——扩展子句中不能包含通配符。我将答案简化为更具可读性/实用性的内容。是的,如果你像你提到的user102008那样做,可能会产生不安全的代码——我已经在我的答案中添加了这个。应该是位置。这就是我要做的。但是,我更喜欢另一种解决方案(特别是当有许多setter和类要重写时)。我同意,如果有多个setter/override,那么编写起来就更具挑战性。就个人而言,我更喜欢将继承保持在最低限度,选择委托/组合方法。@johncarl如何使用委托/组合创建我问题中的两个类?@JohnEricksen-Eclipse和IntelliJ可以自动创建委托的getter和setter。。。