El método constructor es un método especial que tienen todas las clases aunque no lo sepamos. Este método es el primero que se ejecuta cuando instanciamos una clase, se hace de manera automática y tiene como objetivo inicializar las variables del objeto.
Imaginemos que queremos hacer un objeto de tipo Persona. Este objeto tendrá como variables un nombre, unos apellidos y una edad y queremos que esas variables sean distintos entre los distintos objetos que creemos.
Hasta ahora habíamos visto que se crean los objetos inicializando las variables con un valor predefinido y después les cambiamos el valor con los métodos setter porque las variables serán privadas para respetar el encapsulamiento. Pero también habíamos dicho que somos unos vagos, y claro eso con un objeto de 3 variables no es tanto, pero… ¿Y si queremos crear 10 objetos con 8 variables cada una? Pues nos morimos del asco y para eso está el método constructor, gracias al cual podemos pasar los valores de las variables desde el momento de la instanciación facilitándonos mucho la creación de objetos.
Como hacemos muchas veces vamos a ver un ejemplo y después comentamos los detalles.
class Persona(private var nombre: String, private var apellidos: String, private var edad: Int){
fun presentarse(){
println("Hola, me llamo $nombre $apellidos y tengo $edad años.")
}
}
fun main() {
val pepito: Persona = Persona("Pepe", "Perez Garcia", 35)
pepito.presentarse()
}
El constructor sería este fragmento de código: (private var nombre: String, private var apellidos: String, private var edad: Int). Como vemos, se hace añadiendo la declaración de las variables entre paréntesis al lado del nombre de la clase. Así de fácil y sin tener que añadir otro método.
¿Y cómo lo usamos? Pues en la instanciación añadimos los valores que queremos darle entre paréntesis así («Pepe», «Perez Garcia», 35).
Bloque init
En ocasiones podemos querer que existan condiciones a la hora de inicializar un objeto. Igual que pasaba en los métodos setters podemos escribir una lógica en un bloque llamado init, que haga las comprobaciones que quieras. Este bloque se ejecutará automáticamente después del método constructor. Veamos un ejemplo de uso:
class Persona(private var nombre: String, private var apellidos: String, private var edad: Int){
init{
if(edad < 0){
edad = 0
}
}
fun presentarse(){
println("Hola, me llamo $nombre $apellidos y tengo $edad años.")
}
}
fun main() {
val pepito: Persona = Persona("Pepe", "Perez Garcia", -35)
pepito.presentarse()
}
Como vemos en el bloque init hemos introducido un condicional que compruebe que la edad no es negativa, ya que una persona no puede tener edad negativa. Con esto lo que hacemos es que se compruebe la edad y si es menor la pone a 0.
Valores predeterminados en el constructor
Bueno pues vamos a jugar un poco con el constructor. ¿Qué pasa si damos un valor en la declaración de la variable? Pues que si llamamos al constructor sin dar valor a esa variable, el objeto cogerá el valor que le damos en el constructor.
Mira como está hecho en el siguiente ejemplo y como al llamar al constructor no le pasamos el valor de la edad. Entonces el objeto tomará como valor 18 que es el valor que se le da en el constructor.
class Persona(private var nombre: String, private var apellidos: String = "González Díaz", private var edad: Int = 18){
init{
if(edad < 0){
edad = 0
}
}
fun presentarse(){
println("Hola, me llamo $nombre $apellidos y tengo $edad años.")
}
}
fun main() {
val pepito: Persona = Persona("Pepe", "Perez Garcia")
pepito.presentarse()
}
Varios métodos constructores
Antes de ver esto quiero que probéis el siguiente código:
class Persona(private var nombre: String, private var apellidos: String, private var edad: Int){
init{
if(edad < 0){
edad = 0
}
}
fun presentarse(){
println("Hola, me llamo $nombre $apellidos y tengo $edad años.")
}
}
fun main() {
val pepito: Persona = Persona("Perez Garcia", 35)
pepito.presentarse()
}
Nos da error. ¿Verdad? Nos dice el compilador que el segundo parámetro de Persona debe de ser un String y es que el método de poner valores por defecto es muy limitada, siempre al poner parámetros tomará el primer parámetro como el valor de la primera variable, el segundo como la segunda variable, etc. Por lo que en el ejemplo anterior toma como «Perez Garcia» el valor del nombre y el 35 como el valor de apellidos pero y si queremos solo dar el apellido y la edad? Necesitaremos un segundo constructor:
class Persona(private var nombre: String, private var apellidos: String, private var edad: Int){
constructor(apellidos: String, edad: Int): this( "pepe", apellidos, edad)
init{
if(edad < 0){
edad = 0
}
}
fun presentarse(){
println("Hola, me llamo $nombre $apellidos y tengo $edad años.")
}
}
fun main() {
val pepito: Persona = Persona("Perez Garcia", -35)
pepito.presentarse()
}
Como vemos el segundo constructor se escribe así constructor(apellidos: String, edad: Int): this( «pepe», apellidos, edad).
Primero escribes la palabra reservada constructor, después ponemos las variables que pasaremos entre paréntesis (Ojo que aquí no las estamos declarando, por lo que no hay que pones val o var). Despues añadimos : this (lo que estamos haciendo aquí es pasarle el constructor principal) y al final los valores de los parámetros. Donde queramos poner un valor predeterminado se lo damos, y donde queramos que sea el valor de una variable ponemos la variable.
Variables con nombre
Pero esas no son las únicas alternativas, aunque podemos crear varios métodos constructores, también tenemos la opción de poner la variable que queremos nombrar a la hora de instanciar un objeto de las siguiente manera:
class Persona(private var nombre: String = "Juan", private var apellidos: String = "González Díaz",
private var edad: Int = 18){
init{
if(edad < 0){
edad = 0
}
}
fun presentarse(){
println("Hola, me llamo $nombre $apellidos y tengo $edad años.")
}
}
fun main() {
val persona1: Persona = Persona(nombre = "Pepe", edad = 35)
persona1.presentarse()
val persona2: Persona = Persona(apellidos = "Martinez Reverte")
persona2.presentarse()
val persona3: Persona = Persona(edad = 53)
persona3.presentarse()
val persona4: Persona = Persona(apellidos = "Jimenez Luengo", edad = 27)
persona4.presentarse()
val persona5: Persona = Persona("Maria", "Luján Patiño", 31)
persona5.presentarse()
}
Como vemos con un solo constructor al que damos valores por defectos podemos instanciar 5 objetos al que damos distintas combinaciones de variables sin necesidad de métodos constructores adicionales.