【问题标题】:Chaining, returning base objects, and type mismatch to extended classes链接、返回基对象以及与扩展类的类型不匹配
【发布时间】:2013-12-17 15:54:45
【问题描述】:

我遇到过这样的课程。它拥有一个“with”方法,可以让人们将事物链接在一起。

public class Bear {
    protected List<String> names = new ArrayList<String>();
    protected List<String> foods = new ArrayList<String>();

    public Bear withName(String name) {
        names.add(name);
        return this;
    }

    public Bear withFood(String food) {
        foods.add(food);
        return this;
    }
}

// ...somewhere else
Bear b = new Bear().withName("jake").withName("fish");

我发现两个类共享 90% 的相同代码。因此,我在它们之间创建了一个基类,并将 25 个左右的“with”方法转移给它(包括成员变量和所有)。就像这样:

public abstract class Animal {
    protected List<String> names = new ArrayList<String>();

    public Animal withName(String name) {
        names.add(name);
        return this;
    }
}

public class Bear extends Animal {
    protected List<String> foods = new ArrayList<String>();

    public Bear withFood(String food) {
        foods.add(food);
        return this;
    }
}

但是,这现在破坏了一切(并且有很多地方在这两个类的设计中使用它)。

Bear b = new Bear().withName("jake"); // Breaks
bear b2 = new Bear().withFood("fish"); // Fine

给出的错误:

类型不匹配:无法从 Animal 转换为 Bear

显然,当您返回基类 this 时,它返回的是 Bear 类型,并且不会进行任何类型的自动转换。

我有哪些选择来解决/绕过这个问题?

【问题讨论】:

  • 可以,我不清楚哪种方法有效,哪种方法无效。

标签: java types polymorphism


【解决方案1】:

您正在寻找CRTP

public abstract class Animal<T extends Animal<T>> {
    protected List<String> names = new ArrayList<String>();

    public T withName(String name) {
        names.add(name);
        return (T)this;
    }
}

这将给出一个不可避免的未检查强制转换警告,因为类型系统无法阻止您编写 class Cat extends Animal&lt;Dog&gt; {} class Dog extends Animal&lt;Dog&gt;

如果基类中有多个构建器方法,可以通过写private T returnThis() { return (T)this; }来隔离警告。

【讨论】:

    【解决方案2】:

    您的Bear 类扩展了Animal,因此继承了声明为的withName 方法

    public Animal withName(String name) ...
    

    您的方法调用在编译时验证

    Bear b = new Bear().withName("jake");  // Breaks
    

    所有编译器都知道Animal#withName(String) 返回一个Animal。它无法知道在运行时您实际上返回了Bear。所以它不能让你将该值分配给Bear

    您可以执行SLaks suggests 的操作或覆盖Bear 类中的方法并将其返回类型更改为Bear

    @Override
    public Bear withName(String name) {
        names.add(name); // or invoke super method
        return this;
    }
    

    如果您在Bear 类型的引用上调用该方法,该方法的返回类型将为BearSee here for why this works.

    【讨论】:

    • 当我在自己的问题上尝试这种类型的解决方案时,@Override 出现错误:method does not override method from its superclass
    • @rocksNwaves 这意味着您已经更改了方法的签名,使子方法不会覆盖父方法。
    • 我复制并粘贴了这个例子和你的解决方案,它收到了同样的错误。有人告诉我这是因为返回类型不能被覆盖(我刚刚在几分钟前问的一个问题中了解到这一点)。它必须共享相同的返回类型,或者它是重载而不是覆盖的示例......你知道解决这个问题的方法吗?
    • @rocksNwaves 我的答案中的代码假定您正在使用 SLaks 的答案建议的更改,其中 Animal 是通用的,其 withFoodwithName 方法具有相应的类型参数为返回类型。如果 Bear 然后声明为 extends Animal&lt;Bear&gt; ,您可以覆盖该方法,如图所示。
    猜你喜欢
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 1970-01-01
    相关资源
    最近更新 更多