简体   繁体   English

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

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

Here is the gist of what I am trying to do: 这是我要执行的操作的要点:

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;
    }
}

How to write this correctly? 如何正确写呢?

I want to be able to call Dog.makeFive() to give me a List<Dog> or Cat.makeFive() to give me a List<Cat> without having to re-define makeFive() in Dog , Cat , and every other animal class. 我希望能够调用Dog.makeFive()给我一个List<Dog>Cat.makeFive()给我一个List<Cat>而不必在DogCat和每一个中重新定义makeFive()其他动物类。

EDIT: I know the use of static within an abstract class is wrong. 编辑:我知道在abstract类中使用static是错误的。 My question is how to work around that so one is able to call Dog.makeFive() or Cat.makeFive() while only defining makeFive() once. 我的问题是如何解决这个问题,以便仅定义一次makeFive()就能调用Dog.makeFive()Cat.makeFive()

This would satisfy the requirements of your question: 这将满足您的问题的要求:

public interface Animal<T> {

    List<T> makeFive();
}

And then Dog and Cat would both implement Animal 然后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;
   }
}

Alternatively, if you're using Java 8+ you can make an Animal an interface and define makeFive as a default method 另外,如果您使用的是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; } }

You can remove the static keyword from the signature of the function and define the fromInput method as abstract -- 您可以从函数的签名中删除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;
    }
}

You can then use it to implement the invocation as follows -- 然后,您可以使用它来实现调用,如下所示-

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());
        }
    }

}

A super class may not invoke instances of their sub classes unless they are explicitly declared in the super class. 除非在超类中明确声明了它们的子类,否则超类不能调用其子类的实例。 Otherwise the super classes don't even know about them. 否则,超级类甚至都不了解它们。 So for every instance of Dog or Cat that you want in the list, you need to add them from your sub class. 因此,对于列表中所需的Dog或Cat的每个实例,都需要从子类中添加它们。

In your original design, you called makeFive once which contained a single instance of Dog or Cat. 在您的原始设计中,您曾经调用过makeFive,其中包含一个Dog或Cat实例。 How would you expect the parent class of Animal to be able to instantiate different ones with different state? 您如何期望Animal的父类能够实例化具有不同状态的其他类?

Even if you had an interface to support this, each subclass would have to implement that interface to support its type. 即使您有一个支持该接口的接口,每个子类也必须实现该接口以支持其类型。

But you already implement fromInput in each subclass so why not ignore that method and just implement (or override) makeFive()? 但是您已经在每个子类中实现了fromInput,那么为什么不忽略该方法而仅实现(或覆盖)makeFive()? It seems like you're implementing one method to keep from having to implement another. 似乎您正在实现一种方法,而不必实现另一种方法。

Finally, there is one subtle problem with this. 最后,这有一个微妙的问题。 Unless you want List<Animal> to contain the same Dog or Cat object, you also need to copy the newly created List<Animal> to each Dog or Cat instance. 除非您希望List<Animal>包含相同的DogCat对象,否则还需要将新创建的List<Animal>复制到每个DogCat实例。 Otherwise, they will be accessing an empty (and probably null) list. 否则,他们将访问一个空列表(可能为空)。

I want to be able to call Dog.makeFive() to give me a List or Cat.makeFive() to give me a List without having to re-define makeFive() in Dog, Cat, and every other animal class. 我希望能够调用Dog.makeFive()给我一个列表,或致电Cat.makeFive()给我一个列表,而不必在Dog,Cat和所有其他动物类中重新定义makeFive()。

I feel your pain! 我感到你很痛苦! It looks so much like it should be possible, but as the other answers and comments say, Java for whatever reason does not allow it. 看起来似乎很有可能,但是正如其他答案和评论所说,Java出于任何原因都不允许这样做。

So IMHO, the best workaround is to have the single makeFive method in Animal, and pass the destination class to it. 因此,恕我直言,最好的解决方法是在Animal中使用单个makeFive方法,并将目标类传递给它。

So your caller would be : 因此,您的来电者将是:

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

The implementation would be : 实现将是:

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;
    }

}

And finally, have each animal type perform it's initialisation : 最后,让每种动物类型都进行初始化:

class Dog extends Animal {

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

This approach does require that each subclass (Dog, Cat, etc) have a no-argument constructor, but that's a fairly common requirement for various objects in the Java ecosystem. 这种方法确实要求每个子类(狗,猫等)都具有无参数的构造函数,但这是Java生态系统中各种对象的相当普遍的要求。

Alternatively, it could be possible to have the Animal.makeNew method use reflection to find an appropriate constructor, but I personally would advise against that since it becomes difficult to maintain. 另外,也可以让Animal.makeNew方法使用反射来找到合适的构造函数,但是我个人建议不Animal.makeNew ,因为它很难维护。

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

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