【问题标题】:Kotlin higher order function parameters: Passing subtypesKotlin 高阶函数参数:传递子类型
【发布时间】:2019-04-08 11:24:45
【问题描述】:

我在 Kotlin 中遇到了函数参数的问题。我将在一些代码的帮助下解释这个问题。

我创建了一个类层次结构。当我将子类型传递给需要父类型的函数时,没有问题。

open class A (val i: Int)
class B (val j: Int) : A(j)

fun f(x: A){
    print(x)
}

fun test_f(){
    f(A(1))
    f(B(1)) //no problem
}

我试图用函数参数来模仿这个。

fun g(x: (A)->Int){
    print(x)
}

fun test_g(){
    val l1 = { a: A -> a.hashCode()}
    g(l1)

    val l2 = { b: B -> b.hashCode()}
    g(l2) //Error: Type mismatch. Required: (A)->Int, Found: (B)->Int
}

函数类型(B) -> Int 似乎不是(A) -> Int 的子类型。 解决此问题的最佳方法是什么?

我原来的问题是在A.h 中定义一个高阶函数,它以函数z: (A) -> X 作为参数。我想在B 类型的对象上调用h 并传递一个函数z: (B) -> X

更新: 我尝试了具有上限的泛型,但我的问题没有解决。请在下面找到代码:

// Using generics doesn't allow me to pass A.
open class A (val i: Int) {
    fun <M: A> g(x: (M)->Int){
        print(x(this)) // Error: Type mismatch. Expected: M, Found: A 
    }
}

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    您要做的是从函数类型 (B) -&gt; Int (source) 到 (A) -&gt; Int (target) 的转换。这不是一个安全的转换。

    您的源函数(B) -&gt; Int 采用B 的任何实例,但不一定是A 类型的实例。更具体地说,它不能处理A 类型但不是B 类型的所有参数。

    想象你的课程是这样的:

    open class A
    class B : A {
        fun onlyB() = 29
    }
    

    你可以定义一个函数

    val fb: (B) -> Int = { it.onlyB() }
    val fa: (A) -> Int = fb // ERROR
    

    函数fb 将无法对类A 进行操作,因为A 没有onlyB() 函数。因此,您不能将其转换为采用 A 参数的函数类型。


    这个概念被称为逆变,意味着输入参数只能通过变得更具体来约束,而不是更抽象。所以,相反的方向起作用:

    val fa: (A) -> Int = { it.hashCode() }
    val fb: (B) -> Int = fa // OK, every B is also an A
    

    相反,对于返回值,协方差的概念适用。这意味着允许返回值变得更抽象,但不能更具体:

    val fInt: (B) -> Int = { it.onlyB() }
    val fNum: (B) -> Number = fInt // OK, every Int is also a Number
    

    可以在泛型类中利用这些关系,使用 Kotlin 的 in(逆变)和 out(协方差)关键字 -- 请参阅 here 了解详细说明。

    【讨论】:

    • 很好的答案——但是有没有办法避免 invarianceinvariance 之间的任何潜在混淆? (例如'in'variance?)
    • 我认为更好的术语(也称为其他语言)是协变/逆变(参见Wikipedia)。 Kotlin 在 variance 的上下文中谈论 in/out,但没有明确地“in variance/out variance”。我可以相应地编辑帖子。
    • 更好! (虽然现在最后一段中有一个错字,反而破坏了效果……:)
    • 当然,某些用法可以防止任何一种类型的差异。
    【解决方案2】:

    您可以在通用接收器上使用genericsextension function 来解决它。从更新的示例中派生扩展函数:

    fun <T : A> T.g(x: (T)->Int){
        print(x(this))
    }
    

    这样可以确保接收者和给定函数的第一个参数类型相同,要么是A,要么是它的子类型。

    【讨论】:

    • 我试过了,但它不允许我通过 A。检查更新了解详情。
    • @Neo 更新了答案...基本上展示了如何使用通用接收器上的通用函数解决更新后的样本...
    • 是的,这正是我想要的!整洁的方法。
    • 为了帮助其他人看到这个:我在更新中发布的代码与@Roland 的答案之间的重要区别是“T.g”部分,它允许该函数成为泛型类型的扩展函数T.
    猜你喜欢
    • 1970-01-01
    • 2017-07-05
    • 2018-06-27
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    相关资源
    最近更新 更多