Reputation: 1006
Let's assume we got some classes with a hierarchy as follows:
abstract class Foo<T extends A>
class Bar extends Foo<B>
class Hesh extends Foo<C>
abstract class A
class B extends A
class C extends A
I wonder, if it is possible to write a code inside of Foo, that creates objects of type B or C when called from corresponding child class. For example
//inside of Foo body
public void populate() {
//Something like
Class current_t = T.class;
current_t.getConstructor();
//Create an instance of the object using reflection
}
As a result, I would like to have a method in parent class that creates objects, which type is defined in children classes.
Upvotes: 1
Views: 3940
Reputation: 20125
Seelenvirtuose's approaches are cleaner, but a there is a somewhat hacky approach of using getActualTypeArguments
from the superclass type to get the corresponding child class. Once the child's class is obtained, you could use newInstance
in your populate
method to create instances via reflection, as follows:
@SuppressWarnings("unchecked")
public T populate() throws IllegalAccessException, InstantiationException {
return ((Class<T>) (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0])).newInstance();
}
Upvotes: 1
Reputation: 20648
Usually, this is done by adding an abstract method to the parent class with the purpose to create such an instance, and let the child classes implement this method:
public abstract class Foo<T extends A> {
protected abstract T newInstance();
}
public class Bar extends Foo<B> {
@Override protected final T newInstance() { return new B(); }
}
public class Hesh extends Foo<C> {
@Override protected final T newInstance() { return new C(); }
}
This way you have all knowledge about the concrete class that is to be instantiated (no need for reflection).
As a matter of fact, this is called the factory method pattern.
Unfortunately, there is no way to retrieve the concrete parameter type at runtime because they are erased. At least, you are not able to retrieve it in class Foo
.
Another approach would be to provide a constructor that takes a type token:
public abstract class Foo<T extends A> {
private final Class<T> type;
protected Foo(Class<T> type) {
this.type = Objects.requireNonNull(type);
}
private T newInstance() {
return type.newInstance();
}
}
public class Bar extends Foo<B> {
public Bar() {
super(B.class);
}
}
public class Hesh extends Foo<C> {
public Hesh() {
super(C.class);
}
}
The disadvantage here is that those sub classes of A
must have a parameterless constructor.
Upvotes: 4