繁体   English   中英

Java:如何从抽象父类创建新的子类实例?

[英]Java: How to create new child class instance from abstract parent class?

这是我要执行的操作的要点:

abstract class Animal {
    abstract static Animal fromInput(String input); // <- error

    static List<Animal> makeFive() {
        List<Animal> animals = new ArrayList<Animal>();
        animals.add(Animal.fromInput("Input 1"));
        animals.add(Animal.fromInput("Input 2"));
        animals.add(Animal.fromInput("Input 3"));
        animals.add(Animal.fromInput("Input 4"));
        animals.add(Animal.fromInput("Input 5"));
        return animals;
    }
    // ^^^ how to this rewrite this so Dog.makeFive() ^^^ //
    // ^^^ makes Dogs and Cat.makeFive() makes Cats?  ^^^ //
}

class Dog extends Animal {
    @Override static Dog fromInput(String input) {
        Dog dog = new Dog();
        // do some initial dog stuff with input
        return dog;
    }
}

class Cat extends Animal {
    @Override static Cat fromInput(String input) {
        Cat cat = new Cat();
        // do some initial cat stuff with input
        return cat;
    }
}

如何正确写呢?

我希望能够调用Dog.makeFive()给我一个List<Dog>Cat.makeFive()给我一个List<Cat>而不必在DogCat和每一个中重新定义makeFive()其他动物类。

编辑:我知道在abstract类中使用static是错误的。 我的问题是如何解决这个问题,以便仅定义一次makeFive()就能调用Dog.makeFive()Cat.makeFive()

这将满足您的问题的要求:

public interface Animal<T> {

    List<T> makeFive();
}

然后DogCat都会实施Animal

public class Dog implements Animal<DogType> {

   public Dog(){
      super();
   }


   @Override
   public List<DogType> makeFive(){
     // do stuff;
   }

}

public class Cat implements Animal<CatType> {

   public Cat(){
      super();
   }


    @Override
   public List<CatType> makeFive(){
     // do stuff;
   }
}

另外,如果您使用的是Java 8+,则可以将Animal作为接口,并将makeFive定义为default方法

public interface Animal<T> {
    List<T> makeFive();

}
public abstract class Animal { abstract List<? extends Animal> makeFive(); } class Dog extends Animal { @Override List<Dog> makeFive() { return null; } } class Cat extends Animal { @Override List<Cat> makeFive() { return null; } }

您可以从函数的签名中删除static关键字,并将fromInput方法定义为abstract

abstract class Animal<T> {
    abstract T fromInput(String input);

    List<T> makeFive() {
        List<T> animals = new ArrayList<>();
        animals.add(fromInput("Input 1"));
        animals.add(fromInput("Input 2"));
        animals.add(fromInput("Input 3"));
        animals.add(fromInput("Input 4"));
        animals.add(fromInput("Input 5"));
        return animals;
    }
}

class Dog extends Animal<Dog> {
    @Override
    Dog fromInput(String input) {
        Dog dog = new Dog();
        // do some initial dog stuff with input
        return dog;
    }
}

class Cat extends Animal<Cat> {
    @Override
    Cat fromInput(String input) {
        Cat cat = new Cat();
        // do some initial cat stuff with input
        return cat;
    }
}

然后,您可以使用它来实现调用,如下所示-

public class Solution {

    public static void main(String[] args) {
        /*
         * Dog(s)
         */
        List<Dog> dogs = new Dog().makeFive();
        for (Dog dog : dogs) {
            System.err.println(dog.getClass());
        }

        /*
         * Cat(s)
         */
        List<Cat> cats = new Cat().makeFive();
        for (Cat cat : cats) {
            System.err.println(cat.getClass());
        }
    }

}

除非在超类中明确声明了它们的子类,否则超类不能调用其子类的实例。 否则,超级类甚至都不了解它们。 因此,对于列表中所需的Dog或Cat的每个实例,都需要从子类中添加它们。

在您的原始设计中,您曾经调用过makeFive,其中包含一个Dog或Cat实例。 您如何期望Animal的父类能够实例化具有不同状态的其他类?

即使您有一个支持该接口的接口,每个子类也必须实现该接口以支持其类型。

但是您已经在每个子类中实现了fromInput,那么为什么不忽略该方法而仅实现(或覆盖)makeFive()? 似乎您正在实现一种方法,而不必实现另一种方法。

最后,这有一个微妙的问题。 除非您希望List<Animal>包含相同的DogCat对象,否则还需要将新创建的List<Animal>复制到每个DogCat实例。 否则,他们将访问一个空列表(可能为空)。

我希望能够调用Dog.makeFive()给我一个列表,或致电Cat.makeFive()给我一个列表,而不必在Dog,Cat和所有其他动物类中重新定义makeFive()。

我感到你很痛苦! 看起来似乎很有可能,但是正如其他答案和评论所说,Java出于任何原因都不允许这样做。

因此,恕我直言,最好的解决方法是在Animal中使用单个makeFive方法,并将目标类传递给它。

因此,您的来电者将是:

List<Dog> dogList = Animal.makeFive(Dog.class);

实现将是:

public abstract class Animal {

    public abstract void initialise(String input);

    static <T extends Animal> List<T> makeFive(Class<T> clazz) {
        List<T> animals = new ArrayList<T>();
        animals.add(makeNew(clazz, "Input 1"));
        animals.add(makeNew(clazz, "Input 2"));
        animals.add(makeNew(clazz, "Input 3"));
        animals.add(makeNew(clazz, "Input 4"));
        animals.add(makeNew(clazz, "Input 5"));
        return animals;
    }

    private static <T extends Animal> T makeNew(Class<T> clazz, String input) {
        T newAnimal;
        try {
            newAnimal = clazz.newInstance();
            newAnimal.initialise(input);
        } catch (InstantiationException | IllegalAccessException e) {
            newAnimal = null;  // Change This !!!  Maybe throw exception, or add "throws" to method declaration
        }
        return newAnimal;
    }

}

最后,让每种动物类型都进行初始化:

class Dog extends Animal {

    @Override
    public void initialise(String input) {
        // do some initial dog stuff with input
    }
}

这种方法确实要求每个子类(狗,猫等)都具有无参数的构造函数,但这是Java生态系统中各种对象的相当普遍的要求。

另外,也可以让Animal.makeNew方法使用反射来找到合适的构造函数,但是我个人建议不Animal.makeNew ,因为它很难维护。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM