简体   繁体   English

如何使用反射和Guava TypeToken实例化参数化类的Class对象

[英]How to instantiate a Class object for a parameterized class using reflection and Guava TypeToken

I'm trying to pull off some Guava TypeToken magic for a class which is parameterized with a generic type parameter, but I am getting a ClassNotFoundException when trying to instantiate it through the Class.forName(String) method. 我正在尝试为使用通用类型参数进行参数化的类提供一些Guava TypeToken魔术,但是尝试通过Class.forName(String)方法实例化该类时却遇到了ClassNotFoundException

public class EnumeratedMenu<E extends Enum<E>,T extends EnumeratedBean<E>> {

    private final TypeToken<EnumeratedMenu<E,T>> typeToken =
            new TypeToken<EnumeratedMenu<E,T>>(getClass()) { };

    private Class<T> getBean() {

        TypeToken<?> genericBeanParam = typeToken.resolveType(EnumeratedMenu.class.getTypeParameters()[1]);

        try {
            //This generates a ClassNotFoundException because of the generic type parameter that EnumeratedBean has.
            return (Class<T>)Class.forName(genericBeanParam.getType().getTypeName());
        } catch(ClassNotFoundException e) {
            log.error("Unable to find the class definition for {}", genericBeanParam.getType().getTypeName());
            log.catching(e);
        }
    }
}

I am getting this exception: 我收到此异常:

"Unable to find the class definition for EnumeratedBean<ContextMenuOption$ContextType> " “无法找到EnumeratedBean<ContextMenuOption$ContextType>的类定义”

ContextMenuOption$ContextType is the runtime type of the generic enum parameter for EnumeratedBean . ContextMenuOption$ContextTypeEnumeratedBean的通用enum参数的运行时类型。 How do I get the class object I want? 如何获得所需的类对象?

Clarification: 澄清:

I need to construct and instance of the class to use with a builder object that will construct a menu option like this below: 我需要构造一个类的实例并与一个生成器对象一起使用,该对象将构造一个如下所示的菜单选项:

T optionBean = beanFactory.create(beanClass);

IEnumeratedMenuOptionBuilder<E,T> builder = new EnumeratedMenuOptionBuilder<>(optionBean);
builder.setDriver(getDriver()).setTimeoutInSeconds(getTimeoutInSeconds())
        .setEnumerationFunction(getEnumerationFunction());

What are you actually trying to do here? 您实际上想在这里做什么?

You're never going to be able to get a Class<T> object that represents a generic type like EnumeratedBean<ContextMenuOption$ContextType> , because Class can only represent a raw type like EnumeratedBean . 您将永远无法获得代表诸如EnumeratedBean<ContextMenuOption$ContextType> Class的通用类型的Class<T>对象,因为Class仅能代表诸如EnumeratedBean的原始类型。

It's possible that there's some other way to achieve the result you want, but it's not clear what that result you want is currently. 可能还有其他方法可以达到您想要的结果,但是目前尚不清楚您想要的结果是什么。 For example, what do you want to use the Class you're returning for? 例如,您想使用要返回的Class吗? Perhaps it's possible to do that without the intermediate Class object. 如果没有中间的Class对象也许可以做到这一点

Edit: 编辑:

Since it looks like what you wanted was to be able to create an instance of T , here's an example of how you could do it using TypeToken : 因为看起来您想要的是能够创建T的实例,所以下面是使用TypeToken

public class EnumeratedMenu<E extends Enum<E>, T extends EnumeratedBean<E>> {

  // Just get the type of T, that's what we care about here
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {};

  private T create() {
    try {
      // Using the raw type Class object for the token to
      // get at its default constructor.
      return typeToken.constructor(typeToken.getRawType().getConstructor())
          .invoke(null);
    } catch (Exception e) {
      // ...
    }
  }
}

This is impossible to do, as there is always just one single Class , regardless of parameterizations. 这是不可能做到的,因为始终只有一个Class ,而不管参数设置如何。 This is how Java generics work with type erasure. 这就是Java泛型与类型擦除一起工作的方式。 For detailed explanation of how and why, you may want to read: 有关如何以及为什么的详细说明,您可能需要阅读:

http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html

But as to instantiation you will simply need to use the Class you already have; 但是关于实例化,您只需要使用您已经拥有的Class and cast generic type signature the way you want. 并按照您想要的方式转换泛型类型签名。

This means, for example, that you can actually do: 例如,这意味着您可以实际执行以下操作:

List<String> strings = new ArrayList<String>();
List<Integer> numbers = (List<Integer>)(List<?>) strings;
numbers.add(Integer.valueOf(12));

without runtime exceptions. 没有运行时异常。 There is no real runtime information limiting contents; 没有实际的运行时信息限制内容。 only implicit casting. 仅隐式转换。 You would, however, get a runtime exception if you tried: 但是,如果尝试以下操作,则将获得运行时异常:

String str = strings.get(0);

because while underlying List happily accepts any java.lang.Object s, access code has "hidden" cast, which will trigger exception. 因为底层List愉快地接受任何java.lang.Object ,但是访问代码具有“隐藏”强制转换,这将触发异常。

So, instantiation may work something like: 因此,实例化可能类似于:

List<String> s = (List<String>) ArrayList.class.newInstance();

but using whatever generic type you want. 但是使用您想要的任何通用类型。

EDIT: 编辑:

And if Guava's TypeToken is needed, its javadocs suggest you should either get raw class with getRawType() , or perhaps directly create instance with constructor() . 并且,如果需要Guava的TypeToken ,它的javadocs建议您要么使用getRawType()获取原始类,要么直接使用constructor()创建实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM