如何将Kotlin扩展函数参数约束为与扩展类型相同?

时间:2016-09-20 14:10:55

标签: generics kotlin

我想在泛型类型T上编写扩展方法,其中匹配类型约束方法参数。

我想要编译:

"Hello".thing("world")

但不是这样,因为42不是字符串:

"Hello".thing(42)

此定义不起作用,因为Any

满足T
fun <T> T.thing(p: T) {}

3 个答案:

答案 0 :(得分:12)

正如@Alexander Udalov所述,它不可能直接执行,但有一种解决方法,您可以在其他类型上定义扩展方法,如下所示:

data class Wrapper<T>(val value: T)

val <T> T.ext: Wrapper<T> get() = Wrapper(this)

fun <T> Wrapper<T>.thing(p: T) {
    println("value = $value, param = $p")
}

通过以上编译:

"abc".ext.thing("A")

但下一次失败

"abc".ext.thing(2)

使用:

Kotlin: Type inference failed: Cannot infer type parameter T in fun <T> Wrapper<T>.thing(p: T): Unit
None of the following substitutions
receiver: Wrapper<String>  arguments: (String)
receiver: Wrapper<Int>  arguments: (Int)
can be applied to
receiver: Wrapper<String>  arguments: (Int)

根据@hotkey的建议,似乎应该可以避免使用具有以下扩展属性的显式Wrapper类型:

val <T> T.thing: (T) -> Any? get() = { println("extension body") }

然后将其用作"abc".thing("A"),但也会失败。令人惊讶的是,以下编译"abc".thing.invoke("A")

答案 1 :(得分:8)

据我所知,这在Kotlin 1.0中是不可能的。跟踪器(firstsecond)中有几个关于类似用例的问题,第一个提出的解决方案在将来也可能会有所帮助。

答案 2 :(得分:4)

改进@ miensol的解决方法并使其在视觉上与函数调用相同:

val <T> T.foo: (T) -> SomeType get() = { other -> ... }

这是一个提供lambda的扩展属性,可以使用相同类型T的参数立即调用,如下所示:

"abc".foo(1) // Fail
"abc".foo("def") // OK

不幸的是, seems to be a bug in the compiler 会阻止您撰写"abc".thing("abc"),但"abc".thing.invoke("abc")("abc".thing)("abc)都可以正常运行并过滤掉非字符串。