【问题标题】:ClassCastException with generic varargs?带有通用可变参数的 ClassCastException?
【发布时间】:2019-10-12 14:54:49
【问题描述】:

我有一个通用接口

{
void evaluate( TYPE... things );
}

我有一个带有签名的通用方法

<TYPE> void genericmethod( INTERFACE<TYPE> interfase, TYPE thing )
{
//the following line throws the ClassCastException
interfase.evaluate( thing );
}

PCard 是我项目中的一个名称。

所以最后的调用是

INTERFACE<PCard> interfase = new WorkingImplementation<PCard>();
PCard pcard = new PCard();

这似乎在泛型方法中抛出 ClassCastException,当它尝试调用将单个 pcard 转换为 varargs 数组时。

genericmethod( interfase, pcard );

没有调用泛型方法,直接调用接口,问题就解决了。

interfase.evaluate( pcard );

什么导致异常(内部)? (我个人猜测是java不堪重负)

异常消息:

java.lang.ClassCastException: 类 [Ljava.lang.Object;不能转换为类 [Llib.cardgame.CG$PCard; ([Ljava.lang.Object; 在加载器“bootstrap”的模块 java.base 中;[Llib.cardgame.CG$PCard; 在加载器“app”的未命名模块中)

【问题讨论】:

标签: java generics


【解决方案1】:

简答:泛型和数组不能混用。请改用List&lt;? extends TYPE&gt; things

泛型只是一个编译器技巧。它们在运行时不存在。数组在运行时具有不同的类型,例如 String[]Number[]。但是由于泛型类型在运行时不存在,编译器不可能生成生成泛型类型数组的代码。 (对 varargs 方法的调用会隐式创建一个数组来保存 varargs 参数。)

在您的情况下,编译器会对此发出警告,并生成尽可能接近您想要的代码:每当调用 varargs 方法时创建的隐式数组的生成代码将是 Object[] (因为TYPE,我猜,没有上限)。

如果您打开所有编译器警告,您将收到通知。正确处理所有编译器警告(意思是,不要使用@SuppressWarnings)将保证您不会像现在这样遇到令人费解的意外。

我推荐Gilad Bracha’s generics tutorial。我发现它对于理解泛型的工作原理非常宝贵。

【讨论】:

  • 如果该教程还不够,并且您想要挑战并质疑自己对泛型的理解,请通过angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html 深入研究:-D
  • 目前为止我所知道的。我的问题是关于为什么将泛型接口调用包装到另一个泛型方法中会导致代码中断,而直接调用泛型可变参数是有效的。两种情况的处理方式有所不同。
  • 因为当你有INTERFACE&lt;PCard&gt; interfase时,编译器看到类型是PCard并且生成方法调用为interfase.evaluate(new PCard[] { pcard });。但是在genericmethod的内部,&lt;TYPE&gt;可以是任何东西,所以编译器只能将方法调用生成为interfase.evaluate(new Object[] { thing });。没有涉及可变参数的安全解决方法;只需使用一个列表。
  • 所以这里的真正教训是 Java 数组和泛型不能很好地匹配,编译器已经尽最大努力让它在运行时工作,即使我想尝试它可能不会起作用,所以我只需要使用 L 并使用性能较低的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
  • 2012-02-22
相关资源
最近更新 更多