链接,返回基础对象,以及类型与扩展类不匹配

时间:2013-12-17 15:54:46

标签: java types polymorphism

我遇到过这样的课程。它拥有一种“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

显然,当你返回基类时,它返回一个Bear类型,并且不进行任何类型的自动转换。

我有什么方法可以解决/绕过这个问题?

2 个答案:

答案 0 :(得分:2)

您正在寻找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<Dog> {} class Dog extends Animal<Dog>

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

答案 1 :(得分:0)

您的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.