ClassCastException:java.lang.Class无法强制转换为java.lang.reflect.ParameterizedType

时间:2014-07-18 15:50:44

标签: java

我正在尝试使用泛型,主要是为了练习和我有一点想法。 目前我遇到问题:ClassCastException:java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType。 我发现的大多数主题来自发布非常具体代码的用户。这让我很难理解。我试图让问题变得尽可能简单,但仍然与我的实际问题有关。我希望有人可以提供帮助。

酒吧班:

class Bar { }

Foo类:

class Foo<T> {

  ArrayList<Object> list;

  Foo(int initialCapacity) {
    init(initialCapacity);
  }

  void init(int initialCapacity) {
    list = new ArrayList<Object>(initialCapacity);

    for (int i = 0; i < initialCapacity; i++) {
      try {
        list.add(((Class)((ParameterizedType)this.getClass().
          getGenericSuperclass()).getActualTypeArguments()[0]).newInstance());
      }
      catch (InstantiationException e) {

      }
      catch (IllegalAccessException e) {

      }
    }


  }

}

使用:

Foo<Bar> com = new Foo<Bar>(100);

5 个答案:

答案 0 :(得分:4)

你似乎对Java中的泛型感到困惑。

Java并没有像在C#中那样实现Generics:你无法在运行时检索泛型类型的类型信息,因为它在编译后根本就没有保存(参见:{ {3}})

这意味着如果我声明一个List<MyObject>类型的变量,那么我不知道它是否是MyObject的列表,因为类型信息丢失了,这个列表的类只是List,这是没有实现ParameterizedType。

保存泛型类型信息的唯一时间是在编译时知道类型定义:

public class MyList extends List<MyObject> {}

这里,MyList.class.getGenericSuperclass()应该实现ParameterizedType。我担心你在这里尝试做的事情是不可能的,或者至少,可以工作,如果在继承Foo的类上调用init,例如:

public class StringFoo extends Foo<String> {
    public StringFoo(int initialCapacity) { 
        super(initialCapacity); 
    }
}

StringFoo foo = new StringFoo(100);

答案 1 :(得分:2)

错误的根源是示例中的thisFoo<String>的实例,其超类是非参数化类型java.lang.Object。因此getGenericSuperclass()正在为您Object.class而不是ParameterizedType

您尝试使用的模式类似于type token等库使用的“gson”技巧,您可以在其中定义泛型类型的匿名子类。它是为了捕获通用超类签名中的类型参数。如果你做了

,你的代码就可以了
Foo<Bar> com = new Foo<Bar>(100) {};

空括号使它成为一个匿名类,其通用超类是代表ParameterizedType的{​​{1}}。

请注意,只有在使用非参数化类文字实例化type参数时,此技巧才有效。如果你有像

这样的方法
Foo<Bar>

它会失败,因为static <E> Foo<E> makeFoo(E element) { Foo<E> f = new Foo<E>(100) {}; // do something with element } 将是((ParameterizedType)this.getClass().getGenericSuperclass()) .getActualTypeArguments()[0]而不是TypeVariable。类似地

Class

会失败,因为这里的第一个实际类型参数是Foo<List<String>> listFoo = new Foo<List<String>>(5){} 而不是ParameterizedType

答案 2 :(得分:0)

要回答您的原始问题:您正在尝试将Type类型的对象转换为类型Class。由于TypeClass的超类型,因此会导致异常 - 这是一个令人困惑的句子。

这是我的第二点:除非你真的需要,否则使用反射。这是java的黑魔法!同样正如Snaipe指出的那样,这是最重要的事情,因为类型擦除反射不会帮助你解决与泛型相关的任何问题。

答案 3 :(得分:0)

只有在您Foo的子类化时,您才能尝试做的事情,提供T类型定义中捕获的Foo的实际值:

class StringFoo extends Foo<String> {}

然后init应该按预期工作。如果没有在类型定义中捕获T的实际值,则无法在运行时恢复T的值。

答案 4 :(得分:0)

这是一个解决方案

    public TO getTo() throws Exception{
      if (to == null) {
        try{
            to = ((Class<TO>)((ParameterizedType)this.getClass().
                       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
        }catch(ClassCastException cce){
            cce.printStackTrace();
            to = ((Class<TO>)((ParameterizedType)(((Class<TO>)
                    this.getClass().getAnnotatedSuperclass().getType()).getGenericSuperclass()))
                        .getActualTypeArguments()[0]).newInstance();
        }   
      }
      return to;
    }