Supongamos que tengo el siguiente código.
open class Parent class Child: Parent() fun <T: Parent> foo(obj: T): T = obj inline fun <T: Parent> foo(block: () -> T): T = foo(block()) fun <T> bar(obj: T): T = obj inline fun <T> bar(block: () -> T): T = bar(block()) fun main() { /* Compile error: Overload resolution ambiguity. All these functions match. public inline fun <T : Parent> foo (block: () → TypeVariable(T)): TypeVariable(T) defined in examples in file example.kt public fun <T : Parent> foo (obj: TypeVariable(T)): TypeVariable(T) defined in examples in file example.kt */ foo { Child() } // Works bar { "something" } }
La primera llamada de función ( foo
) me da un error de compilación que dice que la resolución de sobrecarga es ambigua. Pero, ¿por qué es esto cierto si el bloque es de tipo () -> T
y este no es un subtipo de Parent
?
¿No debería ocurrir este error en la segunda llamada de función ( bar
)? ¿Por qué no ocurre?
Supongo que el problema aquí es con la inferencia de tipos. En cada uno de sus ejemplos, confía en la inferencia de tipos para dos cosas:
T
, y En el caso de foo
, la resolución de la sobrecarga depende de conocer el valor del parámetro genérico T
Eso es porque T
está acotado. El valor real de T
puede afectar si es válido llamar a este método o no, dependiendo de si T
resulta ser un subtipo de Parent
.
El problema es que T
debe inferirse en función del valor del parámetro que se pasa, y el tipo de parámetro que se pasa debe inferirse en función de alguna información sobre qué sobrecarga se ha seleccionado y/o cuál es el valor de T
es! Sospecho que esto crea una dependencia circular que evita que se complete la resolución de sobrecarga.
Puede solucionarlo proporcionando un valor para la firma lambda o para el parámetro de tipo genérico:
foo<Parent> { Child() } // works foo({ Child() } as () -> Parent) // works
Extraer la lambda a una variable también lo soluciona, porque hace que el compilador infiera un tipo para la variable de inmediato:
val lambda = { Child() } // type is inferred as () -> Child foo(lambda) // works, because lambda already has an inferred type
El problema no surge con la bar
porque en ese caso, T
no está acotado. No importa lo que T
termine siendo, no afectará si se permite llamar o no a esa sobrecarga en particular. Eso significa que el compilador no necesita inferir un valor para T
antes de realizar la resolución de sobrecarga.
Parece un error en el compilador (todavía presente en 1.6.0-M1).
Dos soluciones aquí:
foo<Child> { Child() }
Irónicamente, hacer lo mismo con la parte de trabajo lo romperá (pero el error no será por ambigüedad, el compilador está bastante seguro de qué sobrecarga llamará: la incorrecta):
/* Compile error: Type mismatch. Required: () → String Found: String */ bar<() -> String>({ "something" })
val lambda = { Child() } foo(lambda)
Esto arreglará la parte rota también:
val block = { "something" } bar<() -> String>(block)