Consider this class with two functions, one with Int
argument, the other with a generic one:
class C<K, V> {
// ...
operator fun f(index: Int): Pair<K, V> = ...
operator fun f(key: K): V = ...
}
When it is parameterized as C<Int, SomeType>
, K
is Int
, and both functions match the calls, resulting into an error:
val m = C<Int, SomeType>()
m.f(1)
Overload resolution ambiguity. All these functions match:
public final fun f(index: Int): SomeType
defined in C
public final fun f(key: Int): Pair<Int, SomeType>?
defined in C
How do I call whichever f
I want in this case?
If you are lucky enough to have different parameter names of the functions, using named arguments will do the trick:
m.f(index = 1) // calls f(index: Int)
m.f(key = 1) // calls f(key: K)
Otherwise, if the parameter names are the same (or defined in Java), one possible workaround is to perform unchecked casts to make the compiler choose the desired option:
To call f(index: Int)
, you can use
@Suppress("UNCHECKED_CAST")
val s = (m as C<*, SomeType>).f(1) as Pair<Int, SomeType>
The cast to C<*, SomeType>
makes K
equivalent to in Nothing, out Any
, meaning that there's no valid argument for f(key: K)
, so the call is naturally resolved to f(index: Int)
, but you need to cast the result back, because otherwise it is Pair<Any, SomeType>
.
To call f(key: K)
, use:
@Suppress("UNCHECKED_CAST")
val s = (m as C<Any, SomeType>).f(1 as Any)
Similarly, the cast to C<Any, SomeType>
changes the signature of the desired function to f(key: Any)
, and to call it, just upcast 1
to Any
.
It's all the same in case of several type parameters clashing (e.g. f(key: K)
and f(value: V)
when K
and V
are both SomeType
), just use named arguments or cast the object to ban one of the functions (in Nothing
) or to make it accept Any
.
Kotlin stdlib uses convention fun fAt(index: Int)
to resolve such cases.