I have a generic class with subclasses:
class Item<T>
{
//
}
class IntItem extends Item<Integer>
{
//
}
I need a method to get an instance of Item from its class. I tried methods like:
<T extends Item<T>> T getItem(Class<T> itemClass)
{
return ...
}
<T> Item<T> getItem(Class<Item<T>> itemClass)
{
return ...
}
But it does not work:
// Does not compile: "inference variable T has incompatible equality constraints Integer,IntItem"
IntItem ii = getItem(IntItem.class);
What is the correct way to do this ?
EDIT
Note that question is only related to the generics/method signatures.
Same question but passing an item as parameter:
IntItem ii = getItem2(anIntItem);
static <T extends Item<E>,E> T getItem2(T item)
{
// Does not compile: "error: incompatible types: Item cannot be converted to T"
return getItem(item.getClass();
}
>Solution :
Firstly, your getItem method needs a separate generic parameter to set on your Item parameter (I’m going to use E). Secondly, just use Class#newInstance:
public static <T extends Item<E>, E> T getItem(Class<T> itemType)
throws InstantiationException, IllegalAccessException {
return itemType.newInstance();
}
You can also omit the extends Item<?> part entirely. While this will allow the method to instantiate instances of other types, it will still work perfectly for Item types.
If in the future you wanted to add parameters to your item constructors, you could use Class#getConstructor(Class<?>...) or Class#getConstructors() to find the constructor you want to use, then
Constructor#newInstance(Object...) to create your instance. Here are two examples:
// This example does NOT work with null arguments
public static <T> T getItem(Class<T> itemType, Object... arguments)
throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Class<?>[] parameterTypes = Arrays.stream(arguments).map(Object::getClass).toArray(Class[]::new);
return getItem(itemType, parameterTypes, arguments);
}
// This example works with null arguments
public static <T> T getItem(Class<T> itemType, Class<?>[] parameterTypes, Object[] arguments)
throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Constructor<T> constructor = itemType.getConstructor(parameterTypes);
return constructor.newInstance(arguments);
}