【问题标题】:How to get the generic type at runtime?如何在运行时获取泛型类型?
【发布时间】:2010-10-15 10:27:54
【问题描述】:

这是我的代码: 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;
    }
}

那么,我的代码有什么问题?

【问题讨论】:

标签: java generics reflection


【解决方案1】:

这里有些混乱。由于类型擦除,您无法从运行时参数化类型中获取类型信息,例如:

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

但是,您可以通过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));
    }

    // ...
}

更新:至于是否推荐这样做,虽然这会起作用,但每当类声明发生细微变化时,这对运行时问题很敏感。作为开发人员,您应该正确记录它。作为完全不同的选择,您可以使用多态性。

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

    // ...
}

并相应地实施UserExecutor

class UserExecutor extends AbstractExecutor<User> {

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

    // ...
}

【讨论】:

【解决方案2】:

我认为你应该使用getActualTypeParameters;因为getTypeParameters 不是指在您当前的实例化中放置E 的内容,而是指E 本身(描述它是如何有界的,等等)。

为了获得ParameterizedType,您应该首先使用getGenericSuperclass

更新:但上述方法仅适用于当前对象派生自具有泛型参数实例化的泛型类的情况,例如:

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

应该返回String.class

【讨论】:

  • 确切地说:getGenericSuperclass() 仅当该类不是泛型类时才会为您提供有意义的结果,因此: new LinkedList().getClass().getGenericSuperclass() 获胜'对你来说还不够,但是: new LinkedList(){}.getClass().getGenericSuperclass() 没问题 - 注意到区别了吗?通过添加 {},我创建了 LinkedList 的子类。
  • @iirekm true,那么我想一个对象无法检测到它是如何被反射声明的,但它可以“从外部”完成(例如使用Field.getGenericType())......
【解决方案3】:

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

【讨论】:

  • 不完全正确:阅读“fortran”的解决方案以及我对它的评论。
  • 嘿嘿,我也想过一次......擦除意味着在编译时你可以忽略通用限制,因为运行两个实例的字节码是相同的,但是关于实例如何的信息声明被保留。
【解决方案4】:

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

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

这样您就可以知道参数的类别,而不会给子类的客户带来太多负担。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    相关资源
    最近更新 更多