Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create new object of Generic Type T from a parametrized List<T>

Tags:

java

generics

I have following java sample class:

public class TestClass {

    public static <T> void method(List<T> objects) throws Exception {
        for (int i = 0; i < objects.size(); i++) {
            // Create new object of the same class
            T obj = (T) objects.get(i).getClass().newInstance();
        }
    }
}

It is producing a compiler warning:

Type safety: Unchecked cast from capture#1-of ? extends Object to T

I can perfectly obtain the exact T object, if I just do:

T obj = objects.get(i);

Which it knows perfectly is of Type T.

Why I am not able to create a new instance of that class? How could I fix it?

(I am not looking for a response of type: 'Add @SuppressWarnings("unchecked")')

like image 771
Mayday Avatar asked Apr 20 '18 10:04

Mayday


People also ask

How can we create generic objects in JavaScript?

In JavaScript, there is the ability to create a generic anonymous object to serve this purpose. It can either be created using new Object() or the shorthand { ... } syntax, and can then be given any properties or methods that are needed.


2 Answers

This happens because getClass() returns a Class<? extends Object>. You cannot (safely) cast the wildcard ? to anything, not even a generic class type. This is a limitation of Java Generics. But since you can be sure this warning is not a problem you can safely ignore or suppress it.


But here is a workaround, many applications and libraries do this:

public static <T> void method(List<T> objects, Class<T> clazz) throws Exception {
    for (int i = 0; i < objects.size(); i++) {
        // Create new object of the same class
        T obj = clazz.newInstance();
    }
}
like image 63
Impulse The Fox Avatar answered Nov 15 '22 13:11

Impulse The Fox


Generics are erased at runtime. So invoking getClass() on a declared T element of the list returns a <? extends Object> class instance.

Object.getClass() states indeed :

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

To invoke newInstance() and create an instance of T you need to know the Class instance which you want to instantiate but note that the List can contain any subclass of T, so passing a class as parameter may not be enough.

For example :

List<Animal> list = new ArrayList<>();
list.add(new Lion());
list.add(new Rabbit());
list.add(new Turtle());

Which one invoked now ?

method(list, Lion.class); 
method(list, Rabbit.class);
method(list, Turtle.class);

No one as anyone will compile ! So it is clearly not enough.
So I think that it makes more sense to keep your original code by suppressing the warning. In this way you ensure that you will create an instance of the same class that the actual element in the list :

public static <T> void method(List<T> objects) throws Exception {
    for (int i = 0; i < objects.size(); i++) {
        // Create new object of the same class
        @SuppressWarnings("unchecked")
        T obj = (T) objects.get(i).getClass().newInstance();
        System.out.println(obj);
    }
}
like image 23
davidxxx Avatar answered Nov 15 '22 13:11

davidxxx