与 Kotlin 泛型的混淆
Confusion with Kotlin generics
我是 Kotlin 的新手,我正在尝试编写一些相当简单的代码,但是我不知道如何使用泛型来让它工作。
我有一个 Handler
特征,代表事物的处理程序。 我无法更改处理程序的代码,因为它来自库。
trait Handler<T> {
fun handle(result: T)
}
下面所有的代码都在我的掌控之中-
User
是一个开放的 class,它有子 class,例如 AdminUser
和 GuestUser
等
一个名为 AdminUserAction
的特征会创建一个 AdminUsers 列表,然后将该列表传递给 List<AdminUser>
-
的处理程序
trait AdminUserAction {
fun then(handler: Handler<List<AdminUser>>)
}
现在我想为 User
而不是 AdminUser
传递一个 AdminUserAction
处理程序。假设处理程序只是记录用户的姓名,而不对管理员特定的属性做任何事情。
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames)
}
但是,这段代码给了我一个 TypeMismatch。
由于 Handler 是 List<T>
类型并且是不可变的,因此前面的代码应该是完全安全的,但是编译器无法识别它。
如果我可以访问 Handler 的代码,我可以执行以下操作并且它会起作用 -
trait Handler<in T> {
fun handle(result: T)
}
但是,正如我之前所说,我无法修改 Handler,因为它来自库。此外,似乎 hacky 必须这样做,因为 Handler 的类型是完全通用的,应该也可用于其他类型的处理程序。
我试过 subclassing Handler 并使用它 -
trait ListHandler<in T>: Handler<List<T>> { }
但是现在我收到一条错误消息 "Parameter T is declared as 'in' but occurs in 'invariant' position in Handler>"
我试过了 -
trait ListHandler<in T>: Handler<List<in T>> { }
但这给了我更多的错误。
为什么这么混乱?我如何使用泛型来使前面的代码起作用?
编辑:
我可以通过编写一个将 Handler<List<User>>
转换为 Handler<List<AdminUser>>
-
的通用函数来使其工作
fun <T: User> fromGeneric(handler: Handler<User>): Handler<T> {
return object: Handler<T> {
override fun handle(result: List<T>) {
handler.handle(result)
}
}
}
然后-
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(fromGeneric(printAllNames))
}
但是,这看起来太浪费了。尤其要看一下转换函数的主体 fromGeneric
。它什么都没做!然而,为了满足类型,我每次都必须经历使用它的繁琐程序。
有没有更好的方法?技术上是否有可能使 Kotlin 编译器更智能,从而不需要这种类型的变戏法?
有几个解决方案:
将AdminUserAction
的定义改为
trait AdminUserAction {
fun then(handler: Handler<in List<AdminUser>>)
}
或将 AdminUserAction
的定义更改为
trait AdminUserAction {
fun then(handler: Handler<List<User>>)
}
或者像这样投printAllNames
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames as Handler<List<AdminUser>>)
}
我是 Kotlin 的新手,我正在尝试编写一些相当简单的代码,但是我不知道如何使用泛型来让它工作。
我有一个 Handler
特征,代表事物的处理程序。 我无法更改处理程序的代码,因为它来自库。
trait Handler<T> {
fun handle(result: T)
}
下面所有的代码都在我的掌控之中-
User
是一个开放的 class,它有子 class,例如 AdminUser
和 GuestUser
等
一个名为 AdminUserAction
的特征会创建一个 AdminUsers 列表,然后将该列表传递给 List<AdminUser>
-
trait AdminUserAction {
fun then(handler: Handler<List<AdminUser>>)
}
现在我想为 User
而不是 AdminUser
传递一个 AdminUserAction
处理程序。假设处理程序只是记录用户的姓名,而不对管理员特定的属性做任何事情。
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames)
}
但是,这段代码给了我一个 TypeMismatch。
由于 Handler 是 List<T>
类型并且是不可变的,因此前面的代码应该是完全安全的,但是编译器无法识别它。
如果我可以访问 Handler 的代码,我可以执行以下操作并且它会起作用 -
trait Handler<in T> {
fun handle(result: T)
}
但是,正如我之前所说,我无法修改 Handler,因为它来自库。此外,似乎 hacky 必须这样做,因为 Handler 的类型是完全通用的,应该也可用于其他类型的处理程序。
我试过 subclassing Handler 并使用它 -
trait ListHandler<in T>: Handler<List<T>> { }
但是现在我收到一条错误消息 "Parameter T is declared as 'in' but occurs in 'invariant' position in Handler>"
我试过了 -
trait ListHandler<in T>: Handler<List<in T>> { }
但这给了我更多的错误。
为什么这么混乱?我如何使用泛型来使前面的代码起作用?
编辑:
我可以通过编写一个将 Handler<List<User>>
转换为 Handler<List<AdminUser>>
-
fun <T: User> fromGeneric(handler: Handler<User>): Handler<T> {
return object: Handler<T> {
override fun handle(result: List<T>) {
handler.handle(result)
}
}
}
然后-
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(fromGeneric(printAllNames))
}
但是,这看起来太浪费了。尤其要看一下转换函数的主体 fromGeneric
。它什么都没做!然而,为了满足类型,我每次都必须经历使用它的繁琐程序。
有没有更好的方法?技术上是否有可能使 Kotlin 编译器更智能,从而不需要这种类型的变戏法?
有几个解决方案:
将AdminUserAction
的定义改为
trait AdminUserAction {
fun then(handler: Handler<in List<AdminUser>>)
}
或将 AdminUserAction
的定义更改为
trait AdminUserAction {
fun then(handler: Handler<List<User>>)
}
或者像这样投printAllNames
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames as Handler<List<AdminUser>>)
}