11

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?

hotkey
  • 140,743
  • 39
  • 371
  • 326

2 Answers2

14

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.

Community
  • 1
  • 1
hotkey
  • 140,743
  • 39
  • 371
  • 326
0

Kotlin stdlib uses convention fun fAt(index: Int) to resolve such cases.

voddan
  • 31,956
  • 8
  • 77
  • 87
  • 1
    That's true, but that requires some thought in advance to design class `C` preventing possible signature clashes with substituted generics. And if you faced such class and have no way to rename its functions, you have to resort to generic tricks from @hotkey's answer. – Ilya Nov 23 '16 at 01:07