在应用之前选择函数引用:类型推断问题

时间:2019-02-07 19:41:48

标签: generics kotlin functional-programming type-inference kotlin-extension

我想在将函数引用应用于参数之前选择函数引用,但Kotlin无法推断其类型。

假设我有一堂课

class Circle(val radius: Float)

和一个功能

fun sortByRadius(circles: MutableList<Circle>, ascending: Boolean) {
    if (ascending)
        circles.sortBy { it.radius }
    else
        circles.sortByDescending { it.radius }
}

我也想将此函数主体重写为如下形式:

circles.(
    if (ascending) MutableList<Circle>::sortBy
    else MutableList<Circle>::sortByDescending
) { it.radius }

但不起作用。 我也发现了

(MutableList<Circle>::sortBy)(circles) { it.radius }

几乎有效,Kotlin不能推断Float半径的类型;所以我想知道如何指定它。然后我们可以写

(if (ascending) MutableList<Circle>::sortBy
    else MutableList<Circle>::sortByDescending)(circles) { it.radius }

2 个答案:

答案 0 :(得分:1)

很好的问题,而且似乎不可能(使用函数引用)。我试图明确指定期望的类型:

//Change the version according to your compileSdkVersion(Mine is 28)
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'

但错误消息显示

  

类型推断失败。请尝试明确指定类型参数。

the grammar不允许:

val f: MutableList<Circle>.((Circle) -> Float) -> Unit = 
    if (ascending) MutableList<Circle>::sortBy else MutableList<Circle>::sortByDescending

callableReference : (receiverType? '::' (simpleIdentifier | 'class')) ; 后面只能有一个标识符或::。这是一个奇怪的漏洞,因为Java语法确实允许在方法名称之前输入类型参数。

绑定引用也无济于事

class

甚至没有显式类型的作品,这让我很惊讶:

(circles::sortBy) { it.radius } // doesn't compile

但是这样做:

val f: ((Circle) -> Float) -> Unit = circles::sortBy 

所以最终您可以写(需要在lambda周围加上括号)

val f: ((Circle) -> Float) -> Unit = { circles.sortBy(it) }

但可能不想!

答案 1 :(得分:0)

不知道我是否完全理解你的意思,但是我为你的情况写了什么

fun main() {
    val circles = mutableListOf(
        Circle(.1f), Circle(.5f), Circle(3f)
    )

    val sortBy: (MutableList<Circle>, selector: (Circle) -> Float) -> Unit = { list, selector ->
        list.sortBy(selector)
    }

    val sortByDescending: (MutableList<Circle>, selector: (Circle) -> Float) -> Unit = { list, selector ->
        list.sortByDescending(selector)
    }

    val ascending = false
    (if (ascending) sortBy else sortByDescending)(circles) {
        it.radius
    }
    // Or maybe more clear way
    //(if (ascending) sortBy else sortByDescending).invoke(circles) {
    //    it.radius
    //}
}

希望这会对您有所帮助。