我有两个使用非常类似方法的对象,除了一行。例如:
public class Cat extends Animal
public class Dog extends Animal
他们都在抽象类breed
中使用Animal
方法。一个调用new Dog()
,另一个调用new Cat()
。现在我只是在Animal中将它声明为abstract public void breed();
,但有没有办法可以概括它,所以我不必将它作为一个抽象的方法来覆盖它?
答案 0 :(得分:3)
实际上,是的,你可以。你需要使用反射,所以性能可能有点不确定,但这个(未经测试)应该有效:
public abstract class Animal{
public Animal breed(){
return getClass().newInstance();
}
//other methods
}
这将返回实际调用类型的新实例,而不是Animal的类型(实现它的位置)。
这实际上与Prototype Pattern有些相似。虽然在这种情况下,您正在创建一个新实例,而不是复制现有实例。
修改强>
正如@FrankPavageau在注释中指出的那样,不是在构造函数中屏蔽异常,而是可以通过使用
来实现相同的结果public abstract class Animal{
public Animal breed(){
return getClass().getConstructor().newInstance();
}
//other methods
}
这将包装在InvocationTargetException
中抛出的任何异常,这个异常更清晰,可能更容易调试。感谢@FrankPavageau提出的建议。
答案 1 :(得分:3)
有很多方法可以做到这一点,假设breed
你的意思是“创造我的孩子”。
<强>反射强>
首先是使用反射。如果你的类有一个no-args构造函数,那就像调用Class.newInstance一样简单:
public Animal breed() {
try {
return (Animal) getClass().newInstance();
} catch (Exception ex) {
// TODO Log me
return null;
}
}
如果你的所有子类中都没有no-args构造函数,那么你必须在所有子类中都有一个统一的构造函数。例如,如果您有Cat(int, String)
和Dog(int, String)
,那么您需要通过Class.getConstructor
获取构造函数并在其上调用newInstance
:
return (Animal) getClass().getConstructor(int.class, String.class).newInstance(0, "Unnamed");
例如, int
和String
可能是年龄和姓名。这就是你用反射做到这一点的方法。
<强>提供商强>
另一种方法是使用这个简单的界面:
public interface Provider<T> {
T create();
}
然后让您的抽象类在其构造函数中获取此实例:
public abstract class Animal {
private final Provider<Animal> animalProvider;
protected Animal( ... , Provider<Animal> animalProvider) {
// ...
this.animalProvider = animalProvider;
}
public Animal breed() {
return animalProvider.create();
}
}
然后您的子类将Provider<Animal>
传递给超类,该超类将创建子类的新实例:
public class Dog extends Animal {
public Dog( ... ) {
super( ... , new DogProvider());
// ...
}
private static class DogProvider implements Provider<Animal> {
public Animal create() {
return new Dog( ... );
}
}
}
对其他子类也这样做。
注意:如果breed
你的意思是“得到我的类型”,那么你应该编辑你的问题来说明。如果这是你的意思,那么这是一个可行的解决方案:
public abstract class Animal {
protected final Breed breed;
protected Animal( ... , Breed breed) {
// ...
this.breed = breed;
}
public Breed getBreed() {
return breed;
}
}
我建议遵循数据容器方法的get
/ set
约定。 Java具有设计用于处理这些命名约定的bean类,并且它或多或少是许多平台的标准。对于您的子类:
public class Dog extends Animal {
public Dog( ... ) {
super( ... , new Breed( ... ));
// ...
}
}
答案 2 :(得分:2)
不,没有。您必须拥有如下所示的内容
我想你想要在你的抽象类
中public abstract Breed getBreed();
然后在每个子类中都有
public Breed getBreed() {
return new DogBreed();
}
和一个类似的回归猫。
或
在Animal类中有一个名为breed的受保护字段。然后可以在每个子类中初始化它。这将消除对抽象方法的需要。例如
public abstract class Animal {
Breed breed;
...
}
然后在Dog有
public class Dog extends Animal {
public Dog() {
breed = new DogBreed();
}
}
并且有类似于Cat。
在将品种传递给Dog / Cat ctor时可能值得您这样做,以便您可以创建不同品种的Dog对象,而不是将您的模型限制为仅仅一种品种的狗
我不确定Breed是否必须在您的示例中正确建模。你真的想要新的Dog()成为一个品种吗?或者你的意思是类型?在这种情况下,它只是一种动物,返回动物的抽象方法是可行的方法。