Estaba probando algo en Kotlin y ahora no podré dormir bien si no encuentro una respuesta.
Mira esta clase:
data class Person( val name: String, val age: Int? = 0 ) { constructor( secondName: String, secondAge: Int = 5 ) : this( name = "$secondName 2", age = secondAge ) } fun main() { val firstCase = Person("Lorem") // Complaints about ambiguity. val secondCase = Person("Lorem", 42) // Calls secondary constructor. }Quejarse de la ambigüedad en el primer caso es comprensible, pero ¿por qué no sucede lo mismo en el segundo caso? ¿Y por qué decidió llamar al constructor secundario y no al constructor principal?
Ahora, si agrego otro parámetro en el constructor principal con un valor predeterminado :
data class Person( val name: String, val age: Int? = 0, val isAdult: Boolean = false ) { constructor( secondName: String, secondAge: Int = 5 ) : this( name = "$secondName 2", age = secondAge ) } fun main() { val thirdCase = Person("Lorem") // Calls secondary constructor. }Esperaba tener ambigüedad para el tercer caso, al igual que en el primer caso. ¡Pero no! Llama al constructor secundario. ¡¿Por qué?!
Las reglas de resolución de métodos en Kotlin (y en Java) pueden ser un poco arcanas. Afortunadamente, hacen lo obvio en casi todas las situaciones, ¡lo cual es claramente el punto! – pero hay algunos casos de esquina sorprendentes.
El principio general es que elige el método más específico que podría coincidir y solo da un error si no hay un solo ganador.
Entonces, en su segundo caso, los argumentos son String e Int . Los candidatos son el constructor primario (tomando String e Int? ), y el constructor secundario (tomando String e Int ). La última es una coincidencia más específica, porque Int es un subtipo de Int? , por lo que elige ese.
Pero en su primer caso, el único argumento proporcionado es String , que coincide con ambos constructores por igual, por lo que no hay un ganador claro y señala la ambigüedad.
Su tercer caso es aún menos obvio. Sin embargo, la sección de la especificación de Kotlin que analiza cómo elegir el candidato más específico de todas las sobrecargas dice:
Para cada candidato contamos el número de parámetros predeterminados no especificados en la llamada (es decir, el número de parámetros para los que usamos el valor predeterminado). El candidato con el menor número de parámetros predeterminados no especificados es un candidato más específico
Creo que eso es lo que sucede en su tercer caso: elige el constructor secundario (que dejaría solo un parámetro con su valor predeterminado) sobre el primario (que dejaría dos).
Kotlin da más prioridad a una función que es más específica. Considere este ejemplo:
foo(dog) // invokes foo(Dog) fun foo(animal: Animal) {} fun foo(dog: Dog) {} En este caso, foo(dog) también es ambiguo, pero de hecho, usa la función foo(Dog) . Para usar el otro, tenemos que actualizar explícitamente:
foo(dog as Animal) ¿Porque Int es un subtipo de Int? , su constructor secundario es más específico que el constructor principal y es por eso que se prefiere.
En su primer ejemplo, no hay ambigüedad cuando usa dos argumentos, porque selecciona el constructor con el tipo más específico que coincida con su parámetro. ¿No hay ambigüedad para elegir entre el que tiene Int y el que tiene Int? porque Int es más específico que Int? .
Cuando proporciona un solo argumento, es ambiguo ya que no proporciona ningún argumento que pueda ayudar a distinguir si desea Int o Int? .