apply接受的函数参数类型为T.() -> Unitrun 接受的函数参数类型为T.() -> R,但是下面的代码并不会报错

data class TT(
    val id: String,
    val vv: String
)

typealias SS = List<TT>

fun SS.sortedBy(): List<TT> {
    return this.sortedBy { it.id }
}
fun main() {
    val ll = listOf(TT("ASD-a-04", ""), TT("ASD-a-03", ""))
    println(0)
    println(ll.sortedBy { it.id })
    println("ll: $ll")
    println(1)
    val f = SS::sortedBy
    println(f::class.jvmName)
    println(2)
    println(SS::sortedBy.returnType)
    println(3)
    println(ll.apply(SS::sortedBy))
    println("ll: $ll")
    println(4)
    println(ll.run(SS::sortedBy))
    println("ll: $ll")
}

上面的代码中,给applyrun传入了同一个参数,结果都没报错,但是runapply的参数类型是不一样的? 这就涉及到Kotlin的语言特性: Unit coercion。

Coercion to kotlin.Unit

官方文档 大意是:在一个代码块的类型不是Unit而我们期望他的类型是Unit时,放松类型检查,忽略和Unit不匹配的类型。 官方文档里的例子:

fun foo() {
    val a /* : () -> Unit */ = {
        if (true) 42
        // CSB with no last expression
        // Type is defined to be `kotlin.Unit`
    }

    val b: () -> Unit = {
        if (true) 42 else -42
        // CSB with last expression of type `kotlin.Int`
        // Type is expected to be `kotlin.Unit`
        // Coercion to kotlin.Unit applied
    }
}

和上面的SS::sortedBy(function reference)一样,代码块的返回值不是Unit,但是放松了类型检查,没有报错。

UnitConversion

此外,1.4版本还引入了一个新的特性UnitConversion, 这个特性默认关闭,下面的代码会报错:

val ll = listOf(TT("ASD-a-04", ""), TT("ASD-a-03", ""))
val f = SS:sortedBy
println(ll.apply(f)) // The feature "unit conversion" is disabled

build.gradle.kts中加入编译器参数可以开启此特性:

val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
    languageVersion = "1.4"
    freeCompilerArgs = listOf("-XXLanguage:+UnitConversion") // 开启 UnitConversion的编译器参数
}

开启UnitConversion之后,上面的代码就不再报错。

callable reference

开头的代码中用了 SS::sortedBy,这是一个 callable reference,并不是一个代码块,它也支持 coercion to unit。 因为java 8也支持这个特性。相关链接 : YouTrack , jls 15.13.2, StackOverflow answer