簡體   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