Kotlin自定义RollingWindow类数组赋值变量时触发ClassCastException
问题触发原因
- 本质是Kotlin泛型擦除和数组具体化类型特性共同作用的结果:你在
toArray方法里加的@Suppress("UNCHECKED_CAST")只在编译期生效,忽略了类型转换警告,运行时你返回的Array<T>本质还是Array<Any?>(JVM层面对应Object数组),没有真的转换成对应泛型的数组类型。 - 两种调用场景的差异:
直接把toArray()返回值传入assertArrayEquals时,该方法的参数默认匹配Array<Any?>重载,编译器不会插入额外的类型校验逻辑,运行时不会触发转换所以没有报错;
当你主动把返回值赋值给Array<Double>这类具体类型的数组变量时,Kotlin编译器会在赋值行自动插入隐形的类型强转校验代码,运行时JVM发现实际返回的是Object数组,无法转成Double数组,就会抛出ClassCastException。
修复方案
可以根据使用场景选择以下任意一种方案:
方案1:构造数组时传入泛型类引用,生成对应类型的数组
修改RollingWindow构造函数,接收泛型对应的KClass参数,toArray时通过反射构造对应类型的数组,避免无意义的强转:
class RollingWindow<T>(private val maxSize: Int, private val tClass: KClass<T>) { private val buffer = arrayOfNulls<Any?>(maxSize) // 省略push等原有逻辑 fun toArray(): Array<T> { val targetArray = java.lang.reflect.Array.newInstance(tClass.java, buffer.size) as Array<T> // 拷贝buffer中的有效数据到目标数组 buffer.copyInto(targetArray) return targetArray } } // 调用示例 val window = RollingWindow<Double>(10, Double::class)
方案2:用reified泛型简化调用
如果不想每次手动传类引用,可以增加内联的reified泛型构造方法:
class RollingWindow<T> private constructor( private val maxSize: Int, private val tClass: KClass<T> ) { companion object { inline operator fun <reified T> invoke(maxSize: Int): RollingWindow<T> { return RollingWindow(maxSize, T::class) } } // 其他逻辑和方案1一致 } // 调用示例,无需手动传入类引用 val window = RollingWindow<Double>(10)
方案3:返回List替代Array
如果业务上不需要强类型数组,直接返回List可以避免数组的类型校验问题,不需要额外处理类引用:
fun toList(): List<T> { // 按实际业务逻辑过滤有效数据后返回 return buffer.filterNotNull().map { it as T } }
内容的提问来源于stack exchange,提问作者Plee
相关产品推荐
相关产品推荐

