24 - POO - Constructor de la clase

En Kotlin podemos definir un método que se ejecute inicialmente y en forma automática. Este método se lo llama constructor.

El constructor tiene las siguientes características:

  • Es el primer método que se ejecuta.
  • Se ejecuta en forma automática.
  • No puede retornar datos.
  • Se ejecuta una única vez.
  • Un constructor tiene por objetivo inicializar atributos.
  • Una clase puede tener varios constructores pero solo uno es el principal.

Problema 1

Implementar una clase llamada Persona que tendrá como propiedades su nombre y edad.
Plantear un constructor donde debe llegar como parámetros el nombre y la edad.
Definir además dos métodos, uno que imprima las propiedades y otro muestre si es mayor de edad.

Proyecto112 - Principal.kt

class Persona constructor(nombre: String, edad: Int) {
    var nombre: String = nombre
    var edad: Int = edad

    fun imprimir() {
        println("Nombre: $nombre y tiene una edad de $edad")
    }

    fun esMayorEdad() {
        if (edad >= 18)
            println("Es mayor de edad $nombre")
        else
            println("No es mayor de edad $nombre")
    }
}

fun main(parametro: Array<String>) {
    val persona1 = Persona("Juan", 12)
    persona1.imprimir()
    persona1.esMayorEdad()
}

El constructor principal de la clase se lo declara inmediatamente luego de definir el nombre de la clase:

class Persona constructor(nombre: String, edad: Int) {

Podemos ver que no tiene un bloque de llaves con código y podemos asignar los parámetros a propiedades que define la clase:

    var nombre: String = nombre
    var edad: Int = edad

En la función main donde definimos un objeto de la clase Persona debemos pasar en forma obligatoria los datos que recibe el constructor:

    val persona1 = Persona("Juan", 12)

Por eso decimos que el constructor se ejecuta en forma automática y tiene por objetivo inicializar propiedades del objeto que se crea.

La llamada a los otros métodos de la clase no varía en nada a lo visto en el concepto anterior:

    persona1.imprimir()
    persona1.esMayorEdad()

Acotaciones

Palabra clave constructor opcional.

En muchas situaciones como esta la palabra clave constructor es opcional y en forma más concisa podemos escribir la declaración de la clase y su constructor principal con la sintaxis:

class Persona (nombre: String, edad: Int) {

Veremos en conceptos futuros que es obligatorio la palabra constructor cuando agregamos un modificador de acceso (private, protected, public, internal) previo al constructor o una anotación.

Definición de propiedades en el mismo constructor.

Esto también lo implementa Kotlin para favorecer que el programa sea lo más conciso posible (reducir la cantidad de líneas de código)

El programa completo queda ahora con la sintaxis:

class Persona (var nombre: String, var edad: Int) {

    fun imprimir() {
        println("Nombre: $nombre y tiene una edad de $edad")
    }

    fun esMayorEdad() {
        if (edad >= 18)
            println("Es mayor de edad $nombre")
        else
            println("No es mayor de edad $nombre")
    }
}

fun main(parametro: Array<String>) {
    val persona1 = Persona("Juan", 12)
    persona1.imprimir()
    persona1.esMayorEdad()
}

Es importante ver que cuando declaramos el constructor hemos definido las dos propiedades:

class Persona (var nombre: String, var edad: Int) {

No hace falta declararlas dentro de la clase y si lo hacemos nos genera un error sintáctico ya que estaremos definiendo dos veces con el mismo nombre una propiedad:

class Persona (var nombre: String, var edad: Int) {
   var nombre: String = ""
   var edad: Int = 0

Esto no significa que no se vayan a definir otras propiedades en la clase, solo las que se inicializan en el constructor las definimos en el mismo.

Bloque init

Si en algunas situaciones queremos ejecutar un algoritmo inmediatamente después del constructor debemos implementar un bloque llamado init.

En este bloque podemos por ejemplo validar los datos que llegan al constructor e inicializar otras propiedades de la clase.

Modificaremos nuevamente el programa para verificar si en el parámetro de la edad llega un valor menor a cero:

class Persona (var nombre: String, var edad: Int) {

    init {
        if (edad < 0)
            edad = 0
    }

    fun imprimir() {
        println("Nombre: $nombre y tiene una edad de $edad")
    }

    fun esMayorEdad() {
        if (edad >= 18)
            println("Es mayor de edad $nombre")
        else
            println("No es mayor de edad $nombre")
    }
}

fun main(parametro: Array<String>) {
    val persona1 = Persona("Juan", -12)
    persona1.imprimir()
    persona1.esMayorEdad()
}

El bloque init debe ir encerrado entre llaves e implementamos un algoritmo que se ejecutará inmediatamente después del constructor. En nuestro ejemplo si la propiedad edad se carga un valor negativo procedemos a asignarle un cero:

    init {
        if (edad < 0)
            edad = 0
    }

Problema 2

Implementar una clase que cargue los lados de un triángulo e implemente los siguientes métodos: inicializar las propiedades, imprimir el valor del lado mayor y otro método que muestre si es equilátero o no.

Proyecto113 - Principal.kt

class Triangulo (var lado1: Int, var lado2: Int, var lado3: Int){

    fun ladoMayor() {
        print("Lado mayor:")
        when {
            lado1 > lado2 && lado1 > lado3 -> println(lado1)
            lado2 > lado3 -> println(lado2)
            else -> println(lado3)
        }
    }

    fun esEquilatero() {
        if (lado1 == lado2 && lado1 == lado3)
            print("Es un triángulo equilátero")
        else
            print("No es un triángulo equilátero")
    }
}

fun main(parametro: Array<String>) {
    val triangulo1 = Triangulo(12, 45, 24)
    triangulo1.ladoMayor()
    triangulo1.esEquilatero()
}

Definimos tres propiedades en el constructor principal de la clase:

class Triangulo (var lado1: Int, var lado2: Int, var lado3: Int){

Cuando creamos un objeto de la clase Triangulo en la función main le pasamos los valores de los tres lados del triángulo:

fun main(parametro: Array<String>) {
    val triangulo1 = Triangulo(12, 45, 24)
    triangulo1.ladoMayor()
    triangulo1.esEquilatero()
}

Definición de varios constructores

Cuando se define otro constructor aparte del principal de la clase debe implementarse obligatoriamente con la palabra clave constructor, sus parámetros y la llamada obligatoria al constructor principal de la clase. En un bloque de llaves desarrollamos el algoritmo del mismo.

Problema 3

Implementar una clase que cargue los lados de un triángulo e implemente los siguientes métodos: inicializar las propiedades, imprimir el valor del lado mayor y otro método que muestre si es equilátero o no.

Plantear el constructor principal que reciba los valores de los lados y un segundo constructor que permita ingresar por teclado los tres lados.

Proyecto114 - Principal.kt

class Triangulo (var lado1: Int, var lado2: Int, var lado3: Int){

    constructor():this(0, 0, 0) {
        print("Ingrese primer lado:")
        lado1 = readln().toInt()
        print("Ingrese segundo lado:")
        lado2 = readln().toInt()
        print("Ingrese tercer lado:")
        lado3 = readln().toInt()    
    }

    fun ladoMayor() {
        print("Lado mayor:")
        when {
            lado1 > lado2 && lado1 > lado3 -> println(lado1)
            lado2 > lado3 -> println(lado2)
            else -> println(lado3)
        }
    }

    fun esEquilatero() {
        if (lado1 == lado2 && lado1 == lado3)
            println("Es un triángulo equilátero")
        else
            println("No es un triángulo equilátero")
    }
}

fun main(parametro: Array<String>) {
    val triangulo1 = Triangulo()
    triangulo1.ladoMayor()
    triangulo1.esEquilatero()
    val triangulo2 = Triangulo(6, 6, 6)
    triangulo2.ladoMayor()
    triangulo2.esEquilatero()
}

El constructor principal con la definición de tres propiedades es:

class Triangulo (var lado1: Int, var lado2: Int, var lado3: Int){

El segundo constructor debe llamar obligatoriamente al constructor principal antecediendo la palabra clave this y entre paréntesis los datos a enviar:

    constructor():this(0, 0, 0) {

Luego entre llaves el algoritmo propiamente dicho de ese constructor:

    constructor():this(0, 0, 0) {
        print("Ingrese primer lado:")
        lado1 = readln().toInt()
        print("Ingrese segundo lado:")
        lado2 = readln().toInt()
        print("Ingrese tercer lado:")
        lado3 = readln().toInt()    
    }

Ahora cuando creamos un objeto de la clase Triangulo podemos llamar a uno u otro constructor según nuestras necesidades.

Si queremos cargar por teclado los tres lados del triángulo debemos llamar al constructor que no tiene parámetros:

    val triangulo1 = Triangulo()
    triangulo1.ladoMayor()
    triangulo1.esEquilatero()

Si ya sabemos los valores de cada lado del triángulo se los pasamos en la llamada al constructor:

    val triangulo2 = Triangulo(6, 6, 6)
    triangulo2.ladoMayor()
    triangulo2.esEquilatero()

Problemas propuestos

  • Implementar una clase llamada Alumno que tenga como propiedades su nombre y su nota. Al constructor llega su nombre y nota.
    Imprimir el nombre y su nota. Mostrar un mensaje si está regular (nota mayor o igual a 4)
    Definir dos objetos de la clase Alumno.
  • Cofeccionar una clase que represente un punto en el plano, la coordenada de un punto en el plano está dado por dos valores enteros x e y.
    Al constructor llega la ubicación del punto en x e y.
    Implementar un método que retorne un String que indique en que cuadrante se ubica dicho punto. (1º Cuadrante si x > 0 Y y > 0 , 2º Cuadrante: x < 0 Y y > 0, 3º Cuadrante: x < 0 Y y < 0, 4º Cuadrante: x > 0 Y y < 0)
    Si alguno o los dos valores son cero luego el punto se encuentra en un eje.
    Definir luego 5 objetos de la clase Punto en cada uno de los cuadrantes y uno en un eje.
Solución
Proyecto115

class Alumno (val nombre: String, val nota: Int){

    fun imprimir() {
        println("Alumno: $nombre tiene una nota de $nota")
    }

    fun mostrarEstado () {
        if (nota >= 4)
            println("$nombre se encuentra en estado REGULAR")
        else
            println("$nombre no está REGULAR")
    }
}

fun main(parametros: Array<String>) {
    val alumno1 = Alumno("Ana", 7)
    alumno1.imprimir()
    alumno1.mostrarEstado()
    val alumno2 = Alumno("Carlos", 2)
    alumno2.imprimir()
    alumno2.mostrarEstado()
}



Proyecto116

class Punto (val x: Int, val y: Int) {

    fun retornarCuadrante() = when {
        x > 0 && y > 0 -> "Primer cuadrate"
        x < 0 && y > 0 -> "Segundo cuadrante"
        x < 0 && y < 0 -> "Tercer cuadrante"
        x > 0 && y < 0 -> "Cuarto cuadrante"
        else -> "Un eje"
    }
}

fun main(parametro: Array<String>) {
    val punto1 = Punto(12, 3)
    println("La coordenada ${punto1.x}, ${punto1.y} se encuentra en ${punto1.retornarCuadrante()}")
    val punto2 = Punto(-4, 3)
    println("La coordenada ${punto2.x}, ${punto2.y} se encuentra en ${punto2.retornarCuadrante()}")
    val punto3 = Punto(-2, -2)
    println("La coordenada ${punto3.x}, ${punto3.y} se encuentra en ${punto3.retornarCuadrante()}")
    val punto4 = Punto(12, -5)
    println("La coordenada ${punto4.x}, ${punto4.y} se encuentra en ${punto4.retornarCuadrante()}")
    val punto5 = Punto(0, -5)
    println("La coordenada ${punto5.x}, ${punto5.y} se encuentra en ${punto5.retornarCuadrante()}")
}