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) {}
Kotlin extensions ,while they claim to be statically dispatched , they feel like dynamically "attached" , if there is such a thing, or late bounded to the instance.
For doing this we create an extension function for MutableList<> with swap() function. The list object call the extension function (MutableList<Int>. swap(index1: Int, index2: Int):MutableList<Int>) using list. swap(0,2) function call.
Extension functions are a cool Kotlin feature that help you develop Android apps. They provide the ability to add new functionality to classes without having to inherit from them or to use design patterns like Decorator.
The type parameter lets you specify exactly that—instead of “This variable holds a list,” you can say something like “This variable holds a list of strings.” Kotlin's syntax for saying “a list of strings” looks the same as in Java: List<String> . You can also declare multiple type parameters for a class.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With