Is it possible to reflectively instantiate a generic type in Java? Using the technique described here I get an error because class tokens cannot be generic. Take the example
Not quite sure why you're using generics here.
The instantiation of the object using reflection would suggest a general use but presumably you're going to call create
at some point and assign the result to a String
, otherwise why use the generics to control the return type.
But if you wrote the following implementation of Creator:
public class IntegerCreator implements Creator<Integer>
{
public Integer create()
{
...
}
}
And passed it in as a argument you'd get a ClassCastException when calling create
and assigning the result.
You don't need that line. Nor do you need the constructor as you're just using the default one. Just instantiate the class directly:
public static void main(String[] args) throws Exception {
Class<?> someClass = Class.forName(args[0]);
Creator<String> creator = (Creator<String>) someClass.newInstance();
}
If you insist, you'll only be able to get halfway there:
public static void main(String[] args) throws Exception {
Class<?> someClass = Class.forName(args[0]);
Class<? extends Creator> creatorClass = someClass.asSubclass(Creator.class);
Constructor<? extends Creator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
Creator<String> creator = (Creator<String>) creatorCtor.newInstance((Object[]) null);
}
The generic information is lost in runtime. There is no runtime equivalent of a Creator<String>.class. You could create a type between Creator and StringCreator which fixes the generic type:
public interface Creator<T> {
T create();
}
public interface StringCreator extends Creator<String> { }
public class StringCreatorImpl implements StringCreator {
public String create() { return new String(); }
}
public class FancyStringCreator implements StringCreator {
public String create() { return new StringBuffer().toString(); }
}
public static void main(String[] args) throws Exception {
Class<?> someClass = Class.forName(args[0]);
Class<? extends StringCreator> creatorClass = someClass.asSubclass(StringCreator.class);
Constructor<? extends StringCreator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
Creator<String> creator = creatorCtor.newInstance((Object[]) null);
}
But of course you lose a bit of flexibility, because you cannot use the following creator class:
public class AnotherCreator implements Creator<String> {
public String create() { return ""; }
}
This will do what you are trying to do while providing type safety. There's no way to avoid an unchecked warning, but the type checking done here justifies its suppression.
public static void main(String[] args)
throws Exception
{
Class<? extends Creator<String>> clz = load(argv[0], String.class);
Constructor<? extends Creator<String>> ctor = clz.getConstructor();
Creator<String> creator = ctor.newInstance();
System.out.println(creator.create());
}
public static <T> Class<? extends Creator<T>> load(String fqcn, Class<T> type)
throws ClassNotFoundException
{
Class<?> any = Class.forName(fqcn);
for (Class<?> clz = any; clz != null; clz = clz.getSuperclass()) {
for (Object ifc : clz.getGenericInterfaces()) {
if (ifc instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) ifc;
if (Creator.class.equals(pType.getRawType())) {
if (!pType.getActualTypeArguments()[0].equals(type))
throw new ClassCastException("Class implements " + pType);
/* We've done the necessary checks to show that this is safe. */
@SuppressWarnings("unchecked")
Class<? extends Creator<T>> creator = (Class<? extends Creator<T>>) any;
return creator;
}
}
}
}
throw new ClassCastException(fqcn + " does not implement Creator<String>");
}
The main restriction you have to adhere to is that a class in the hierarchy must specify the type parameter. For example class MyCreator implements Creator<String>
. You can't use it with class GenericCreator<T> implements Creator<T>
.
It doesn't currently handle the valid case where you create a new interface interface StringCreatorIfc extends Creator<String>
, and have a class implement that. It could be enhanced to do that, but I'll leave that as an exercise for those inclined.