18

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

Foo bar = new Foo().setX(10).setY(11);

我觉得这很甜蜜,所以我决定试一试。让我的setX()类型函数返回Foo而不是全部void放入。return this;这运作良好。直到我尝试使用继承,这产生了一些惊人的结果。

我将举一个具体的例子:我有两个类,一个Location类有两个字段,x 和 y。另一个类Location3D继承自Location并添加了第三个字段 z。所有字段都使用上述方法作为其设置器。

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

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

工作时

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

没有。

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

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

我也确信有一个比“二传手穿线”更好的术语,它是什么?

4

2 回答 2

23

正如您所指出的,new Location3D().setX(7).setY(6).setZ(5)不起作用的原因是因为setX()and返回and notsetY()的实例。LocationLocation3D

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

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

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

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

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

不幸的是,我知道没有办法避免超类产生的警告,因此是@SuppressWarnings("unchecked")注释。

还值得注意的是,如果您定义子类以使泛型类型参数是不同的类类型,那么您可能ClassCastException会以 .

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

于 2012-12-31T19:15:36.300 回答
10

问题是 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;
    }
}

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

组合方法:

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

和 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;
    }
}
于 2012-12-31T19:19:41.190 回答