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
source to share
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();
}
}
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 signatureproduceCar
and add that instance to the list directly. Since you instantiateCarManufactured
by explicitly specifying a type parameter, then there is no need to store thisClass<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()
oraddCar()
.
source to share