在Java中用抽象类创建新对象

时间:2012-09-22 18:23:14

标签: java abstract-class

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

public class Cat extends Animal
public class Dog extends Animal

他们都在抽象类breed中使用Animal方法。一个调用new Dog(),另一个调用new Cat()。现在我只是在Animal中将它声明为abstract public void breed();,但有没有办法可以概括它,所以我不必将它作为一个抽象的方法来覆盖它?

3 个答案:

答案 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");
例如,

intString可能是年龄和姓名。这就是你用反射做到这一点的方法。

<强>提供商

另一种方法是使用这个简单的界面:

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()成为一个品种吗?或者你的意思是类型?在这种情况下,它只是一种动物,返回动物的抽象方法是可行的方法。