【问题标题】:Confusion with Kotlin generics与 Kotlin 泛型混淆
【发布时间】:2015-07-11 03:22:15
【问题描述】:

我是 Kotlin 的新手,我正在尝试编写一些相当简单的代码,但是我不知道如何使用泛型来使其工作。

我有一个 Handler 特征,它代表事物的处理程序。 我无法更改 Handler 的代码,因为它来自库

trait Handler<T> {
    fun handle(result: T)
}

下面的所有代码都在我的控制之下-

User 是一个开放类,它有AdminUserGuestUser 等子类。

名为AdminUserAction 的特征会创建一个AdminUser 列表,然后将列表传递给List&lt;AdminUser&gt; 的处理程序-

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

现在我想为User 传递一个AdminUserAction 处理程序,而不是AdminUser。假设处理程序只记录用户的姓名,而不对管理员特定的属性做任何事情。

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

然而,这段代码给了我一个TypeMismatch

由于 Handler 的类型是 List&lt;T&gt; 并且是不可变的,所以前面的代码应该是完全安全的,但是编译器无法识别它。

如果我可以访问 Handler 的代码,我可以执行以下操作并且它会起作用 -

trait Handler<in T> {
    fun handle(result: T)
}

但是,正如我之前所说,我无法修改 Handler,因为它来自库。此外,似乎必须这样做hacky,因为 Handler 的类型是完全通用的,并且也应该可用于其他类型的处理程序。

我尝试子类化 Handler 并使用它 -

trait ListHandler<in T>: Handler<List<T>> { }

但是现在我收到一条错误消息,提示 “参数 T 被声明为 'in' 但在 Handler 中出现在 'invariant' 位置>”

我试过了-

trait ListHandler<in T>: Handler<List<in T>> { }

但这给了我更多的错误。

为什么会如此混乱?以及如何使用泛型来使前面的代码工作?

编辑:

我可以通过编写一个将Handler&lt;List&lt;User&gt;&gt; 转换为Handler&lt;List&lt;AdminUser&gt;&gt; 的通用函数来使其工作 -

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 编译器更智能,从而不需要这种类型的杂耍?

【问题讨论】:

    标签: java generics types type-conversion kotlin


    【解决方案1】:

    有几个解决方案:

    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>>)
    }
    

    【讨论】:

    • 解决方案 1 和 2 可能有效,但会限制我在其他情况下使用 AdminUserAction 的能力。解决方案 3 是不可取的,因为我们失去了类型安全性。
    • 关于如何解决问题的任何想法 - trait ListHandler&lt;in T&gt;: Handler&lt;List&lt;T&gt;&gt; { }?这将是 IMO 最巧妙的解决方案
    • 问题最终在于您无法更改 Handler 特征。处理程序实际上是一个消费者(如kotlinlang.org/docs/reference/generics.html 中所述),因此应该将 T 声明为 in。因为它不是,所以无论如何你都需要某种 hack。据我所知,解决方案 1 最接近真正的解决方案。此外,我看不出这如何限制您对 AdminUserAction 的使用。从类型系统的角度来看,这是有道理的。
    • 是的,仍然希望有一个好的解决方案,不需要我定义自己的 Handler 类型,因为那样我就必须继续将 that 类型转换为库 Handler到处都是。
    猜你喜欢
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多