与Kotlin仿制药混淆 [英] Confusion with Kotlin generics
问题描述
我是Kotlin的新手,我正在尝试编写一些相当简单的代码,但我无法弄清楚如何使用泛型来实现它。
我有一个 Handler
trait,它表示事物的处理程序。 我无法更改Handler的代码,因为它来自库 。
trait Handler< T> {
fun handle(result:T)
}
所有代码下面是在我的控制 -
用户
是一个开放类,它有子类,如 AdminUser
和 GuestUser
等。
一个名为 AdminUserAction
会创建一个AdminUser列表,然后将该列表传递给 List< AdminUser>
-
的处理程序
trait AdminUserAction {
fun then(handler:Handler< List< AdminUser>>)
}
现在我想为传递一个
而不是AdminUserAction
处理程序,用户AdminUser
。假设处理程序只记录用户的名称,并且不对管理员指定的属性执行任何操作。fun doIt action:AdminUserAction,printAllNames:Handler< List< User>>){
action.then(printAllNames)
}
然而,这段代码给了我一个TypeMismatch 。
由于Handler的类型是
List< T>
并且是不可变的,前面的代码应该是完全安全的,但是编译器无法弄清楚。
如果我有权访问Handler的代码,我可以执行以下操作并且它可以工作 -
trait Handler<在T> {
fun handle(result:T)
}
然而,之前说过,我不能修改Handler,因为它来自一个库。另外,似乎必须这样做,因为处理程序的类型是完全一般的,并且也可用于其他类型的处理程序。
我尝试了继承Handler并使用它 -
trait ListHandler< in T>:Handler< List< T>> {}
但是现在我得到一个错误,说参数T被声明为'in '但在'Handler>'中的'invariant'位置发生>
我试过 -
trait ListHandler< in T>:Handler< List< T>> {}
但是这给了我更多的错误。
为什么这很混乱?我怎样才能使用泛型来获得前面的代码?
$ b编辑:
我可以通过编写一个将
Handler< List<用户>>
转换为Handler< List< AdminUser> >
-fun< T:User> fromGeneric(handler:Handler< User>):Handler< T> {
return object:Handler< T> {
覆盖有趣的句柄(result:List< T>){
handler.handle(result)
}
}
}
然后 -
fun doIt (action:AdminUserAction,printAllNames:Handler< List< User>>){
action.then(fromGeneric(printAllNames))
}
但是,这看起来很浪费。特别是从通用中查看转换函数
的主体。它正在做什么!然而,我必须经历每次使用它的方法来满足这些类型。
有更好的方法吗?技术上是否可以让Kotlin编译器更加智能化,以便不需要这种类型的混战?
:
将
AdminUserAction
的定义更改为
trait AdminUserAction {
fun then(处理程序: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>>)
}
I'm new to Kotlin and I'm trying to write code which does something fairly simple, however I cannot figure out how to use generics to get it to work.
I have a
Handler
trait which represents a handler for things. I cannot change the code for a Handler as it comes from a library.trait Handler<T> { fun handle(result: T) }
All of the code below is in my control -
User
is an open class that has subclasses such asAdminUser
andGuestUser
etc.A trait called
AdminUserAction
does something to create a List of AdminUsers and then passes the list to a handler forList<AdminUser>
-trait AdminUserAction { fun then(handler: Handler<List<AdminUser>>) }
Now I want to pass an
AdminUserAction
a handler forUser
instead ofAdminUser
. Let's say the handler simply logs the names of the users, and doesn't do anything with Admin specific properties.fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) { action.then(printAllNames) }
However, this code gives me a TypeMismatch.
Since the Handler is of the type
List<T>
and is immutable, the preceding code should be completely safe, however the compiler isn't able to figure it out.If I had access to the code for Handler I could do the following and it would work -
trait Handler<in T> { fun handle(result: T) }
However, as I said before, I cannot modify Handler as it comes from a library. Also, it seems hacky to have to do this because the type of Handler is fully general and should be usable for other kinds of handlers too.
I tried subclassing Handler and using that -
trait ListHandler<in T>: Handler<List<T>> { }
However now I get an error that says "Parameter T is declared as 'in' but occurs in 'invariant' position in Handler>"
I tried -
trait ListHandler<in T>: Handler<List<in T>> { }
But that gives me more errors.
Why is this so confusing? And how can I use generics to get the preceding code to work?
Edit:
I can make it work by writing a generic function that converts a
Handler<List<User>>
intoHandler<List<AdminUser>>
-fun <T: User> fromGeneric(handler: Handler<User>): Handler<T> { return object: Handler<T> { override fun handle(result: List<T>) { handler.handle(result) } } }
And then -
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) { action.then(fromGeneric(printAllNames)) }
But, this seems so wasteful. Especially look at the body of the conversion function
fromGeneric
. It is doing nothing! Yet I have to go through the rigamarole of using it everytime just to satisfy the types.Is there a better way? Is it technically possible to make the Kotlin compiler smarter so that this type jugglery is not needed?
解决方案There are a couple of solutions:
Change the definition of
AdminUserAction
totrait AdminUserAction { fun then(handler: Handler<in List<AdminUser>>) }
or change the definition of
AdminUserAction
totrait AdminUserAction { fun then(handler: Handler<List<User>>) }
or just cast
printAllNames
like thisfun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) { action.then(printAllNames as Handler<List<AdminUser>>) }
这篇关于与Kotlin仿制药混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!