5

我有两个使用非常相似的方法的对象,除了一行。例如:

public class Cat extends Animal
public class Dog extends Animal

而且它们都使用breed抽象类中的方法Animal。一个打电话new Dog(),另一个new Cat()。现在我只是将它声明为abstract public void breed();在 Animal 中,但是有没有一种方法可以概括它,所以我不必让它成为一个被覆盖的抽象方法?

4

3 回答 3

3

有很多方法可以做到这一点,假设breed你的意思是“创造我的孩子”。

反射

首先是使用反射。如果你的类有一个无参数构造函数,这就像调用Class.newInstance一样简单:

public Animal breed() {
    try {
        return (Animal) getClass().newInstance();
    } catch (Exception ex) {
        // TODO Log me
        return null;
    }
}

如果在所有子类中都没有无参数构造函数,则必须在所有子类中都有一个统一的构造函数。例如,如果你有Cat(int, String)and 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();
}

然后让您的抽象类在其构造函数中获取 this 的实例:

public abstract class Animal {
    private final Provider<Animal> animalProvider;

    protected Animal( ... , Provider<Animal> animalProvider) {
        // ...
        this.animalProvider = animalProvider;
    }

    public Animal breed() {
        return animalProvider.create();
    }
}

然后您的子类将 a 传递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( ... ));
        // ...
    }
}
于 2012-09-22T18:53:22.603 回答
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 的建议。

于 2012-09-22T18:30:53.130 回答
2

不,没有。您将必须拥有类似于您所做的事情,如下所示

我想你想在你的抽象类中

public abstract Breed getBreed();

然后在每个子类中都有

public Breed getBreed() {
    return new DogBreed();
}

和一只类似的回归猫。

或者

在 Animal 类中有一个受保护的字段,称为品种。然后可以在每个子类中对其进行初始化。这将消除对抽象方法的需要。例如

public abstract class Animal {
    Breed breed;
...
}

然后在 Dog have

public class Dog extends Animal {
    public Dog() {
        breed = new DogBreed();
    }
}

并且有类似于猫的东西。

在将品种传递给 Dog/Cat ctor 时可能值得您这样做,这样您就可以创建不同品种的 Dog 对象,而不是将您的模型限制为只有一个品种的 Dog

我不确定 Breed 在您的示例中是否必须正确建模。你真的希望 new Dog() 成为一个品种吗?或者你的意思是类型?在这种情况下,它只是一个动物,返回动物的抽象方法是要走的路。

于 2012-09-22T18:25:45.020 回答