I want to write an extension method on a generic type T, where the matched type constrains a method parameter.
I want this to compile:
"Hello".thing("world")
But not this, as 42 is not a String:
"Hello".thing(42)
This definition doesn’t work, because T is satisfied by Any
fun <T> T.thing(p: T) {}
As mentioned by @Alexander Udalov it's not possible to do directly but there's a workaround where you define the extension method on another type like so:
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")
}
With the above the following compiles:
"abc".ext.thing("A")
but the next fails
"abc".ext.thing(2)
with:
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)
As suggested by @hotkey it seems that it should be possible to avoid the need for explicit Wrapper
type with the following extension property:
val <T> T.thing: (T) -> Any? get() = { println("extension body") }
And then use it as "abc".thing("A")
but it also fails. Surprisingly the following does compile "abc".thing.invoke("A")
As far as I know, this is not possible in Kotlin 1.0. There are several issues in the tracker (first, second) about a similar use case, and the solution proposed in the first one is likely going to help here in the future as well.
Improving @miensol's workaround and making it visually identical to function call:
val <T> T.foo: (T) -> SomeType get() = { other -> ... }
This is an extension property that provides a lambda, which can be immediately called with an argument of the same type T
like this:
"abc".foo(1) // Fail
"abc".foo("def") // OK
Unfortunately, there seems to be a bug in the compiler which prevents you from writing "abc".thing("abc")
, but either of "abc".thing.invoke("abc")
and ("abc".thing)("abc)
work well and filter out calls with non-strings.