Java Generics Call Constructor

Suppose I have four classes: Car

, Convertible

, PickupTruck

and CarManufacturer

.

Car

- an abstract class Convertible

and PickupTruck

inherits from:

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

    //Constructor
}

      

Convertible

and PickupTruck

both have unspecified constructors:

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

stores a list of either Convertibles

or PickupTrucks

.

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

      

How can I implement a function produceCar()

that calls a parameterless constructor and adds an object to the list? I tried:

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

      

Return error: Type parameter 'T' cannot be instantiated directly

+3


source to share


2 answers


The same problems were solved here: fooobar.com/questions/15408 / ...

As for the problem, this works:



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

      

+5




You can add Class<T>

in CarsManufacturer

, which will preserve the meta information about the type parameter in Runtime. This may allow you to create an instance T

using the method Class#newInstance()

:

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

}

      

Then you can use it like this:



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

      

While this should work, keep in mind that there are a few notes worth mentioning:

  • I would not use a member Class<T>

    to access type parameter substitution in Runtime. I would rather add a parameter (T instance)

    to the method signature produceCar

    and add that instance to the list directly. Since you instantiate CarManufactured

    by explicitly specifying a type parameter, then there is no need to store this Class<T>

    one because you already have an understanding of what this parameter is.
  • I would rename the method produceCar

    to something more related to what the method does - for example, saveCar()

    or addCar()

    .
+2


source







All Articles