如何扩展一个返回其方法中“this”的类

5
问题很简单,但我不确定是否能够实现...
如果我们有一个类如下:
class A {
    private int foo;

    public A(int bar) {
       this.foo = bar;
    }

    public A setFoo(int bar) {
       this.foo = bar;
       return this;
    }

    public int getFoo() {
       return this.foo;
    }

    public void doSomething() {
       this.foo++;
    }
}

我们可以看到,它只是一个包含私有成员和setter/getter的类。有趣的是,为了允许方法链,setter返回了this
因此,我们可以像这样做:
A a = new A(0);
a.setFoo(1).doSomething();

这里的问题是,当我尝试扩展该类并实现一个接口来添加一些功能时,会出现以下情况:
class B extends A implements I {
    public B(int bar) {
        this.super(bar);
    }

    public void methodI() {
        // whatever
    }
}

在我这样使用它之前,它似乎还好。

B b = new B(1);
b.setFoo(2).methodI();

因为setFoo实际上返回的是A类的一个实例,而不是B类的实例,在A类中没有methodI方法...是否有任何解决方法?谢谢。
顺便说一下,我只是写了一个基本的代码来简单理解,但如果你想了解更多,我正在尝试扩展一些libgdx的基本类(如Math.Vector2Math.Vector3)以实现Poolable

Tenfour04:你说得没错,关于覆盖其值并不需要 reset() 方法,但这不是 Poolable 的重点。 实现 Poolable 的主要目的是避免创建和销毁新对象,从而大量调用 gc。这与性能有关。 想象一下,一个关键函数每秒钟调用60次,在其中使用 Vectors 作为局部变量。它们将被每次创建和销毁,这在桌面上可能还可以,但在移动游戏中可以进行优化。 - danikaze
不,这就是 Pool 的作用,而不是 Poolable。或者你的意思是完全不使用 Pool,而是手动调用重置方法吗? - Xoppa
要使用对象池(Pool),这些对象需要实现Poolable接口...所以我不明白你的观点是什么... - danikaze
换句话说:Pool 可以池化任何类型的对象,而不仅仅是实现了 Poolable 接口的对象。实现 Poolable 接口的唯一原因是如果您需要调用 reset 方法来清除对象引用并可能设置默认值。即使设置默认值也是不必要的,如果您正在创建一个专门处理您的对象类型的 Pool 子类,因为您可以使用 newObject 方法设置默认值。 - Tenfour04
无论如何,在不考虑libgdx的情况下,原问题仍然是一个合法的Java问题...即我想扩展Vector2以接受自定义数据类型的构造函数...或者其他什么... - danikaze
显示剩余8条评论
5个回答

5

Class B可以重写方法setFoo并将返回类型更改为B,因为B是A的更具体版本。重写的方法可以有更具体的返回类型。例如:

class B extends A implements I {
    public B(int bar) {
        this.super(bar);
    }

    public void methodI() {
        // whatever
    }

    @Override
    public B setFoo(int bar) {
       this.foo = bar;
       return this;
    }
}

是的,这是我想到的第一个解决方案,甚至重新实现带有复制粘贴的类,因为我无法重用它...但是...正在寻找一种解决方法,以便我不必重写很多方法(如果您查看libgdx类,则有许多方法在几个类中,例如Vector2、Vector3等。不仅是直接设置器,还有很多方法来执行操作以修改真实类的内部数据) - danikaze
如果你想改变返回类型,你必须重写方法签名。但是你可以通过调用超类的方法实例来节省一些复制和粘贴,而不是重新实现。例如:@Override public B setFoo(int bar) { super.setFoo(bar); return this; } - bhspencer
由于libgdx库是一个jar文件,我无法修改其代码(而且如果它得到更新的话,这可能不是一个好主意),所以我会采用你的答案并将其标记为有效...但我也会尝试使用bedrin提出的答案提交一个pull request,因为我觉得那才是“正确的答案”。 - danikaze

2

您应该在这里使用泛型:

public class A<T extends A<?>> {

    public T self() {
        return (T) this;
    }

    private int foo;

    public A(int bar) {
       this.foo = bar;
    }

    public T setFoo(int bar) {
       this.foo = bar;
       return self();
    }

    public int getFoo() {
       return this.foo;
    }

    public void doSomething() {
       this.foo++;
    }

}

public class B<T extends B<?>> extends A<T> implements I {

    public B(int bar) {
        this.super(bar);
    }

    public void methodI() {
        // whatever
    }
}

现在您将能够使用这样的链式调用:
B b = new B(1);
b.setFoo(2).methodI();

请记住,我并没有创建A类,它已经存在。 - danikaze
由于libgdx库是一个jar文件,我无法修改其代码(而且如果它被更新了,这可能不是个好主意)。所以我会按照bhspencer的答案并将其标记为有效...但我也会尝试使用您的答案进行推送请求,因为我认为这是“正确的”! - danikaze

1

我之前也遇到过这个问题。我认为没有简单的内置解决方案。考虑覆盖所有setter方法:

class B extends A implements I {
    @Override
    public A setFoo(int bar) {
        super.setFoo(bar);
        return this;
    }
}

3
你的意思是指 public B setFoo 方法吗? - thefourtheye
是的,这是我想到的第一个解决方案,甚至可以通过复制粘贴重新实现类,因为我无法重用它...但是...我正在寻找任何解决方法,以便我不必重写大量方法(不仅是直接设置器,还有很多方法来执行操作以修改真实类的内部数据)。 - danikaze

0

为了完整性(即我不认为这是比上面更好的解决方案,只是可能合适),当然也不是你问题标题的答案,但是可能是你问题的一个解决方案。

你可以考虑使用Java 8提供的default方法。它专门用于向现有类添加功能。

// From LibGDX Pool.Poolable
interface Poolable {

    public void reset();
}

// Class A untouched.
class A {

    private int foo;

    public A(int bar) {
        this.foo = bar;
    }

    public A setFoo(int bar) {
        this.foo = bar;
        return this;
    }

    public int getFoo() {
        return this.foo;
    }

    public void doSomething() {
        this.foo++;
    }
}

// Extend Poolable to add the functionality.
interface PoolableVector extends Poolable {

    default void reset() {
        // Not sure what you want to do here.
    }
}

// A Poolable A trivially made.
class B extends A implements PoolableVector {

    public B(int b) {
        super(b);
    }

}

我不是很确定,但首先,我认为你还不能在Android中使用Java 8... 其次,如果你不使用默认方法而是在B类中实现reset,我认为你会得到相同的结果???也就是说,在调用b.setFoo().doSomething()时,尝试在A.setFoo返回的A对象上调用B.doSomething,就像我在问题中解释的那样... 如果我错了,请纠正我... - danikaze

-2
只需将setFoo()方法返回void,然后你就可以这样做:
B b = new B(1); b.setFoo(2); b.methodI();

2
OP想要“return this”特性。 - Srinath Ganesh
在这种情况下,我可以忽略返回值并分两行完成,但我想问一下是否有解决方法来使用该功能... - danikaze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接