En Kotlin, cada clase puede tener un único constructor primario y varios constructores secundarios. La delegación de constructores es un mecanismo en el que un constructor secundario debe llamar obligatoriamente al constructor primario directamente, o a otro constructor secundario de la misma clase (y, en última instancia, al primario). Si la clase hereda de otra, cada constructor secundario de la clase hija debe delegar explícitamente la llamada al constructor de la superclase, si es necesario.
En Java, los constructores pueden llamarse entre sí directamente mediante this() o super(), sobrecargando los constructores en varias combinaciones. En Kotlin, este concepto se ha formalizado: una clase solo puede tener un constructor primario, mientras que los constructores secundarios pueden utilizar la lógica de delegación, que debe indicarse explícitamente.
Una implementación incorrecta de la delegación puede causar errores de compilación: no se puede omitir la llamada al constructor primario si está declarado, o no se puede omitir el super-constructor en una clase heredada, si la clase base no tiene un constructor por defecto. Es importante entender en qué momento se llaman los bloques init, cómo se pasan los parámetros y cómo la delegación afecta al orden de inicialización.
Ejemplo de delegación básica:
class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("Constructor secundario: $name, $age") } }
Si se utiliza herencia:
open class Parent(val name: String) class Child : Parent { constructor(name: String) : super(name) { println("Hijo secundario: $name") } }
¿Puede un constructor secundario no delegar a nada?
No, el compilador exigirá que se llame explícitamente a this(...) o super(...), de lo contrario habrá un error.
¿En qué orden se llevan a cabo las inicializaciones y bloques init, si se utiliza un constructor secundario?
El constructor primario y los bloques init se llaman siempre primero, luego se ejecuta el código de inicialización del constructor secundario.
class Demo(val value: String) { init { println("bloque init") } constructor(value: String, code: Int) : this(value) { println("secundario: $code") } } Demo("kotlin", 7) // salida: // bloque init // secundario: 7
¿Puede un heredero llamar solo al constructor primario del padre, si hay un constructor secundario?
Sí, pero solo explícitamente, a través de super(...), los secundarios no son visibles directamente para los herederos.
En el proyecto, el constructor secundario no llama al primario, la inicialización crítica (por ejemplo, establecer campos obligatorios) no ocurre, lo que provoca errores y caídas en tiempo de ejecución.
Ventajas:
Desventajas:
Todos los constructores secundarios delegan estrictamente a través de this(...), la inicialización necesaria está centralizada en el primario/init, la estructura es transparente para el mantenimiento.
Ventajas:
Desventajas: