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

Java 如何使用反射和Guava TypeToken为参数化类实例化类对象,java,generics,reflection,guava,Java,Generics,Reflection,Guava,我试图为一个用泛型类型参数参数化的类实现一些GuavaTypeToken魔术,但是当我试图通过class.forName(String)方法实例化它时,我得到了一个ClassNotFoundException public class EnumeratedMenu<E extends Enum<E>,T extends EnumeratedBean<E>> { private final TypeToken<EnumeratedMenu<

我试图为一个用泛型类型参数参数化的类实现一些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);
        }
    }
}

你到底想在这里做什么

您永远无法获得表示泛型类型(如
EnumeratedBean
)的
Class
对象,因为
Class
只能表示原始类型(如
EnumeratedBean

可能还有其他方法可以达到你想要的结果,但目前还不清楚你想要的结果是什么。例如,您想使用返回的
做什么?也许在没有中间
对象的情况下可以做到这一点

编辑:

因为看起来您想要的是能够创建
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) {
      // ...
    }
  }
}
公共类枚举菜单{
//只要得到T的类型,这就是我们关心的
private final-TypeToken-TypeToken=new-TypeToken(getClass()){};
private T create(){
试一试{
//使用令牌的原始类型类对象
//获取其默认构造函数。
返回typeToken.constructor(typeToken.getRawType().getConstructor())
.invoke(空);
}捕获(例外e){
// ...
}
}
}

你到底想在这里做什么

您永远无法获得表示泛型类型(如
EnumeratedBean
)的
Class
对象,因为
Class
只能表示原始类型(如
EnumeratedBean

可能还有其他方法可以达到你想要的结果,但目前还不清楚你想要的结果是什么。例如,您想使用返回的
做什么?也许在没有中间
对象的情况下可以做到这一点

编辑:

因为看起来您想要的是能够创建
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) {
      // ...
    }
  }
}
公共类枚举菜单{
//只要得到T的类型,这就是我们关心的
private final-TypeToken-TypeToken=new-TypeToken(getClass()){};
private T create(){
试一试{
//使用令牌的原始类型类对象
//获取其默认构造函数。
返回typeToken.constructor(typeToken.getRawType().getConstructor())
.invoke(空);
}捕获(例外e){
// ...
}
}
}

这是不可能做到的,因为无论参数化如何,始终只有一个
类。这就是Java泛型如何处理类型擦除。
有关如何以及为什么这样做的详细说明,您可能需要阅读:

但是对于实例化,您只需要使用已有的
;并按您想要的方式强制转换泛型类型签名

例如,这意味着您实际上可以:

List<String> strings = new ArrayList<String>();
List<Integer> numbers = (List<Integer>)(List<?>) strings;
numbers.add(Integer.valueOf(12));
因为当底层
List
愉快地接受任何
java.lang.Object
s时,访问代码具有“隐藏”类型转换,这将触发异常

因此,实例化的工作方式可能类似于:

List<String> s = (List<String>) ArrayList.class.newInstance();
List s=(List)ArrayList.class.newInstance();
但是使用任何你想要的泛型类型

编辑:


如果需要Guava的
TypeToken
,它的javadocs建议您要么使用
getRawType()
获取原始类,要么直接使用
constructor()
创建实例,这是不可能的,因为无论参数化如何,总是只有一个
类。这就是Java泛型如何处理类型擦除。
有关如何以及为什么这样做的详细说明,您可能需要阅读:

但是对于实例化,您只需要使用已有的
;并按您想要的方式强制转换泛型类型签名

例如,这意味着您实际上可以:

List<String> strings = new ArrayList<String>();
List<Integer> numbers = (List<Integer>)(List<?>) strings;
numbers.add(Integer.valueOf(12));
因为当底层
List
愉快地接受任何
java.lang.Object
s时,访问代码具有“隐藏”类型转换,这将触发异常

因此,实例化的工作方式可能类似于:

List<String> s = (List<String>) ArrayList.class.newInstance();
List s=(List)ArrayList.class.newInstance();
但是使用任何你想要的泛型类型

编辑:


如果需要Guava的
TypeToken
,它的javadocs建议您要么使用
getRawType()
获取原始类,要么直接使用
constructor()创建实例

我试图将对象实例化为参数化类型,并使用生成器类设置其字段,该生成器类将使用bean字段中的值来构造菜单选项。@Selena底层的
对象从不因泛型类型而异:例如,
List
List
完全相同(请随意测试,以验证通过
List.getClass()
获得的实际
Class
对象是否相同)。这也意味着通过反射进行实例化是完全相同的;大多数泛型类型都是由编译器插入类型转换来处理的,并且没有实际的运行时差异。这意味着您将无法构造不同的类:它们不存在。您需要使用已擦除的现有类型
Class
@StaxMan:ok。那么如何从Guava类型标记中获取类型擦除类的实例呢;虽然您可以使用“constructor()”来实例化传递的类型?@StaxMan:谢谢。这就是我需要的。因此,您应该将此细节添加到您的a中