2017-07-19 6 views
3

Angenommen, ich habe vier Klassen: Car, Convertible, PickupTruck und CarManufacturer.Java Generics Call Constructor

Car ist die abstrakte Klasse, die Convertible und PickupTruck vererben:

public abstract class Car { 
    private String name; 
    private String colour; 

    //Constructor 
} 

Convertible und PickupTruck beide parameterlos Konstrukteure haben:

public class Convertible extends Car { 
    private boolean roofUnfolded; 

    public Convertible() { 
     super("Convertible", "Red"); 
     this.roofUnfolded = false; 
    } 
} 

public class PickupTruck extends Car { 
    private double capacity; 

    public PickupTruck() { 
     super("Pickup Truck", "Black"); 
     this.capacity = 100; 
    } 
} 

CarManufacturer speichert eine Liste der entweder Convertibles oder PickupTrucks.

public class CarManufacturer <T extends Car>{ 
    private List<T> carsProduced = new LinkedList<>(); 
} 

Wie kann ich eine Funktion implementieren produceCar(), die den parameterlosen Konstruktor aufruft und fügt das Objekt in der Liste? Ich habe versucht:

public void produceCar(){ 
    this.carsProduced.add(new T()); 
} 

den Fehler zurückkehrend: Type parameter 'T' cannot be instantiated directly

Antwort

5

Die gleichen Fragen wurden hier gelöst: https://stackoverflow.com/a/36315051/7380270

In Bezug auf das Problem, das funktioniert:

public class CarManufacturer <T extends Car> { 
    private Supplier<T> carType; 
    private List<T> carsProduced = new LinkedList<>(); 

    public CarManufacturer(Supplier<T> carType) { 
     this.carType = carType; 
    } 

    public void produceCar() { 
     this.carsProduced.add(carType.get()); 
    } 

} 

public class Main { 
    public static void main(String[] args) { 
     CarManufacturer<Convertible> convertibleCarManufacturer = new CarManufacturer<>(Convertible::new); 
     convertibleCarManufacturer.produceCar(); 
    } 
} 
2

Sie Class<T> zum CarsManufacturer hinzufügen können, die die Meta-Informationen über den Typ-Parameter zur Laufzeit wird erhalten. Dies könnte erlauben Sie T zu instanziiert, indem die Class#newInstance() Methode:

public class CarManufacturer<T extends Car> { 

    private List<T> carsProduced = new LinkedList<>(); 

    private Class<T> clazz; 

    public CarManufacturer(Class<T> clazz) { 
     this.clazz = clazz; 
    } 

    public void produceCar() throws IllegalAccessException, InstantiationException { 
     this.carsProduced.add(clazz.newInstance()); 
    } 

} 

Sie können es dann wie folgt verwenden:

CarManufacturer<Convertible> carManufacturer = new CarManufacturer<>(Convertible.class); 
carManufacturer.produceCar(); 

Auch wenn dies funktionieren sollte, denken Sie daran, dass es nur wenige Hinweise das ist erwähnenswert:

  • ich würde nicht das Class<T> Mitglied verwenden, nur um den Zugriff auf den Typ-Parameter Ersatz bei Runtime. Ich würde lieber einen (T instance) Parameter zur produceCar Methodensignatur hinzufügen und diese Instanz direkt zur Liste hinzufügen. Da Sie die CarManufactured durch explizite Angabe des Typparameters instanziieren, müssen Sie Class<T> nicht beibehalten, da Sie bereits wissen, was der Parameter ist.
  • Ich würde die produceCar Methode zu etwas mehr im Zusammenhang mit dem, was die Methode tut - zum Beispiel saveCar() oder addCar() umbenennen.
+0

Nun gibt es bessere Möglichkeiten als Reflexionen verwenden, aber immer noch eine gültige Antwort. – Flown