Subclassing a class 以更改 Kotlin 中类型参数的方差
Subclassing a class to change the varience of a type parameter in Kotlin
本题来源于。请在那里查看有关此问题背后动机的更多信息。
我有一个 class 接受无约束类型参数
trait Handler<T> {
fun handle(result: T)
}
我需要创建一个 Handler
实例,其中 T
是 List<O>
,因此是不可变的。我的想法是 subclass Handler
并将其注释为消费者(即使用 in
)-
trait ListHandler<in T>: Handler<List<T>> { }
但是这给了我一个错误,说 "Parameter T is declared as 'in' but occurs in 'invariant' position in Handler<List<T>
"
错误是什么意思,有什么办法解决它吗?
这个错误意味着,就编译器所知,您的声明可能会导致运行时失败。请记住,在检查 ListHandler
时,编译器不知道 Handler
中定义了哪些成员,它所知道的只是 Handler
已成功进行类型检查,并且其签名表明类型参数是不变的。这个是必须的,因为Handler
后面可能改了重新编译不用重新编译ListHandler
:
trait Handler<T> {
fun handle(result: T)
fun get() : T
}
有了这样的声明,写一段代码就很容易了:
// an ill-behaved ListHandler
class HandlerImpl<T> : ListHandler<T> {
private var storage: List<T> = listOf()
override fun handle(result: List<T>) { storage = result }
override fun get(): List<T> = storage
}
// Code using it, that breaks
fun main(args: Array<String>) {
val anyHandler = HandlerImpl<Any>()
anyHandler.handle(listOf(1, 2, 3))
val problematic: ListHandler<String> = anyHandler
// The following line requires an element to be a String, but it is an Int
problematic.get()[0].length()
}
结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
查看完整代码 here。
本题来源于
我有一个 class 接受无约束类型参数
trait Handler<T> {
fun handle(result: T)
}
我需要创建一个 Handler
实例,其中 T
是 List<O>
,因此是不可变的。我的想法是 subclass Handler
并将其注释为消费者(即使用 in
)-
trait ListHandler<in T>: Handler<List<T>> { }
但是这给了我一个错误,说 "Parameter T is declared as 'in' but occurs in 'invariant' position in Handler<List<T>
"
错误是什么意思,有什么办法解决它吗?
这个错误意味着,就编译器所知,您的声明可能会导致运行时失败。请记住,在检查 ListHandler
时,编译器不知道 Handler
中定义了哪些成员,它所知道的只是 Handler
已成功进行类型检查,并且其签名表明类型参数是不变的。这个是必须的,因为Handler
后面可能改了重新编译不用重新编译ListHandler
:
trait Handler<T> {
fun handle(result: T)
fun get() : T
}
有了这样的声明,写一段代码就很容易了:
// an ill-behaved ListHandler
class HandlerImpl<T> : ListHandler<T> {
private var storage: List<T> = listOf()
override fun handle(result: List<T>) { storage = result }
override fun get(): List<T> = storage
}
// Code using it, that breaks
fun main(args: Array<String>) {
val anyHandler = HandlerImpl<Any>()
anyHandler.handle(listOf(1, 2, 3))
val problematic: ListHandler<String> = anyHandler
// The following line requires an element to be a String, but it is an Int
problematic.get()[0].length()
}
结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
查看完整代码 here。