我遇到过这样的课程。它拥有一种“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类型,并且不进行任何类型的自动转换。
我有什么方法可以解决/绕过这个问题?
答案 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
类型的引用上调用该方法,则该方法的返回类型为Bear
。 See here for why this works.