How to get the generic type at runtime?

2019-01-04 02:25发布

This is my code: The ExecutorImp extends AbstractExecutor which extract the same execute logics of its implementers(ExecutorImp is one case),when calling the execute() method of ExecutorImp, it will call the method in its supertype,but the supertype (the AbstractExcutor) should know another class binding to the implementer(in the example, it is the User class):

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

abstract class AbstractExecutor<E> {
    public void execute() throws Exception {
        ArrayList<E> list = new ArrayList<E>();
        // here I want to get the real type of 'E'
        Class cl = this.getClass().getTypeParameters()[0].getGenericDeclaration().getClass();
        Object o = cl.getConstructor(String.class).newInstance("Gate");
        list.add((E) o);
        System.out.println(format(list));
    }
    public abstract String format(ArrayList<E> list);
    public abstract String getType();
}

public class ExectorImp<E> extends AbstractExecutor<User> {
    @Override
    public String getType() {
        return "user";
    }
    @Override
    public String format(ArrayList<User> list) {
        StringBuffer sb = new StringBuffer();
        for (User u : list) {
            sb.append(u.toString() + " ");
        }
        return sb.toString();
    }
    public static void main(String[] args) throws Exception {
        new ExectorImp().execute();
    }
}
class User {
    String name;
    public User(String name) {
        this.name = name;
    }
}

SO, what is the problem with my codes?

4条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-04 02:54

Usual approach to fix the problem is to slightly change the code. Define constructor on the base class accepting Class<E> parameter. Assign this parameter to internal field.

On the subclass define constructor without parameters and call super(User.class) from there.

This way you will know class of argument without much overburden for clients of subclasses.

查看更多
【Aperson】
3楼-- · 2019-01-04 02:57

There's some confusion here. Due to type erasure you can't get type information from the runtime parameterized type like:

Class<E> cls = E.getClass(); // Error.
E e = new E(); // Error.

However, you can obtain compiletime parameterized type information from class, field and method declaration by ParameterizedType#getActualTypeArguments().

abstract class AbstractExecutor<E> {

    public void execute() throws Exception {
        List<E> list = new ArrayList<E>();
        Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        E e = cls.getConstructor(String.class).newInstance("Gate");
        list.add(e);
        System.out.println(format(list));
    }

    // ...
}

Update: as to whether this is recommended or not, although this will work, this is sensitive to runtime problems whenever minor changes in the class declaration occur. You as developer should document it properly. As a completely different alternative, you can make use of polymorphism.

abstract class AbstractExecutor<E> {

    public void execute() throws Exception {
        List<E> list = new ArrayList<E>();
        E e = create("Gate");
        list.add(e);
        System.out.println(format(list));
    }

    public abstract E create(String name);

    // ...
}

and implement UserExecutor accordingly.

class UserExecutor extends AbstractExecutor<User> {

    @Override
    public User create(String name) {
        return new User(name);
    }

    // ...
}
查看更多
走好不送
4楼-- · 2019-01-04 03:00

I think you should use getActualTypeParameters; as getTypeParameters does not refer to what has been put in your current instantiation in place of E, but to E itself (to describe how is it bounded, etc.).

In order to get the ParameterizedType you should use getGenericSuperclass first.

update: but the above only works if the current object is derived from a generic class with the generic argument instantiated, like:

class StringList extends ArrayList<String> {
    public Type whatsMyGenericType() {
        return ((ParameterizedType)getGenericSuperClass()).getActualTypeParameters()[0];
    }
}

should return String.class.

查看更多
Melony?
5楼-- · 2019-01-04 03:16

I don't think you could get the generic type at runtime. The generic type is a restriction that applies at compile time. As I remember at runtime there is no difference between a generic collection and a collection without a generic type.

查看更多
登录 后发表回答