Kotlin高阶函数参数:传递子类型

时间:2019-04-08 11:24:46

标签: kotlin

我在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作为参数的高阶函数。我想在类型为h的对象上调用B并传递一个函数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 
    }
}

2 个答案:

答案 0 :(得分:2)

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

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

通过这种方式,可以确保接收器和给定函数的第一个参数类型相同,它是A或其子类型。

答案 1 :(得分:2)

您要尝试的是从功能类型(B) -> Int source )到(A) -> Int target )的转换。这不是安全的转换。

您的源函数(B) -> 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参数的函数类型。


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

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(convarivariance)和out(covariance)关键字在泛型类中利用这些关系-有关详细说明,请参见here