Java 如何在运行时获取泛型类型?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3941384/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 07:17:38  来源:igfitidea点击:

How to get the generic type at runtime?

javagenericsreflection

提问by hguser

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):

这是我的代码: ExecutorImp 扩展了 AbstractExecutor ,它提取了其实现者的相同执行逻辑(ExecutorImp 是一种情况),当调用 ExecutorImp 的 execute() 方法时,它将调用其超类型中的方法,但超类型(AbstractExcutor ) 应该知道另一个绑定到实现者的类(在示例中,它是 User 类):

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?

所以,我的代码有什么问题?

采纳答案by BalusC

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().

但是,您可以从类、字段和方法声明中获取编译时参数化类型信息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 UserExecutoraccordingly.

并相应地实施UserExecutor

class UserExecutor extends AbstractExecutor<User> {

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

    // ...
}

回答by fortran

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

我认为你应该使用getActualTypeParameters; asgetTypeParameters不是指在您当前的实例化中放置了什么来代替E,而是指E它本身(描述它是如何有界的,等等)。

In order to get the ParameterizedTypeyou should use getGenericSuperclassfirst.

为了得到ParameterizedType你应该getGenericSuperclass先使用。

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.

应该返回String.class

回答by Seitaridis

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.

我认为您无法在运行时获得泛型类型。泛型类型是在编译时适用的限制。我记得在运行时,泛型集合和没有泛型类型的集合之间没有区别。

回答by oiavorskyi

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.

解决问题的通常方法是稍微更改代码。在接受Class<E>参数的基类上定义构造函数。将此参数分配给内部字段。

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

在子类上定义没有参数的构造函数并super(User.class)从那里调用。

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

通过这种方式,您将了解参数类,而不会为子类的客户带来太多负担。