与Kotlin仿制药混淆 [英] Confusion with Kotlin generics

查看:156
本文介绍了与Kotlin仿制药混淆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是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 as AdminUser and GuestUser etc.

A trait called AdminUserAction does something to create a List of AdminUsers and then passes the list to a handler for List<AdminUser> -

trait AdminUserAction {
    fun then(handler: Handler<List<AdminUser>>)
}

Now I want to pass an AdminUserAction a handler for User instead of AdminUser. 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>> into 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)
        }
    }
}

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 to

trait AdminUserAction {
    fun then(handler: Handler<in List<AdminUser>>)
}

or change the definition of AdminUserAction to

trait AdminUserAction {
    fun then(handler: Handler<List<User>>)
}

or just cast printAllNames like this

fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
    action.then(printAllNames as Handler<List<AdminUser>>)
}

这篇关于与Kotlin仿制药混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆