Mayday
Mayday

Reputation: 5146

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

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")')

Upvotes: 5

Views: 1674

Answers (3)

davidxxx
davidxxx

Reputation: 131466

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);
    }
}

Upvotes: 4

Strikegently
Strikegently

Reputation: 2461

Why I am not able to create a new instance of that class?

You are. Your code will compile and it will work as you expect, but the compiler isn't 100% sure of that because of type erasure which is why you get a warning. The important thing to note here is that warnings are not the same as errors.

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

If you look at any Java code base that uses generics and reflection in conjunction with each other, you will see suppressed warnings for type safety. That's just a fact of life, unfortunately.

Upvotes: 1

janssen-dev
janssen-dev

Reputation: 2771

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();
    }
}

Upvotes: 11

Related Questions