Kotlin: Programación Orientada a Objetos

Kotlin implementa Programación Orientada a Objetos con enfoques modernos. Domina clases, objetos, herencia, polimorfismo y encapsulación en Kotlin.

Aprende Kotlin GRATIS y certifícate

La programación orientada a objetos (POO) es un paradigma esencial en muchos lenguajes de programación modernos, y Kotlin no es la excepción. Kotlin ofrece una implementación clara y concisa de los principios de POO, integrando características que facilitan la escritura de código limpio y mantenible. A continuación, exploraremos los conceptos fundamentales de la POO en Kotlin y cómo aplicarlos en tus proyectos.

Clases y objetos en Kotlin

En Kotlin, las clases se definen utilizando la palabra clave class. Una clase es un molde a partir del cual se crean objetos, que son instancias concretas de esa clase.

Definición de una clase

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

En este ejemplo, Persona es una clase con dos propiedades: nombre y edad. La palabra clave val indica que nombre es una propiedad de solo lectura, mientras que var indica que edad es mutable.

Creación de objetos

Para crear una instancia de la clase Persona:

val persona = Persona("Ana", 30)
println("Nombre: ${persona.nombre}, Edad: ${persona.edad}")

Este código creará un objeto persona con el nombre "Ana" y edad 30.

Constructores en Kotlin

Kotlin proporciona un constructor primario y constructores secundarios para inicializar objetos.

Constructor primario

El constructor primario se define junto al nombre de la clase:

class Coche(val marca: String, val modelo: String)

Constructores secundarios

Si necesitas más de un constructor, puedes definir constructores secundarios utilizando constructor:

class Coche {
    val marca: String
    val modelo: String

    constructor(marca: String, modelo: String) {
        this.marca = marca
        this.modelo = modelo
    }

    constructor(marca: String) {
        this.marca = marca
        this.modelo = "Modelo desconocido"
    }
}

Herencia en Kotlin

La herencia permite crear nuevas clases a partir de clases existentes. En Kotlin, todas las clases son finales por defecto, es decir, no se pueden heredar a menos que se declaren como open.

Declaración de una clase base

open class Animal(val nombre: String) {
    fun comer() {
        println("$nombre está comiendo")
    }
}

Subclase que hereda de una clase base

class Perro(nombre: String) : Animal(nombre) {
    fun ladrar() {
        println("$nombre está ladrando")
    }
}

Aquí, Perro es una subclase de Animal y hereda sus propiedades y métodos.

Modificadores de visibilidad

Kotlin ofrece cuatro modificadores de visibilidad: private, protected, internal y public (este último es el predeterminado).

  • private: Visible solo dentro de la clase.
  • protected: Como private, pero también visible en subclases.
  • internal: Visible dentro del mismo módulo.
  • public: Visible en todas partes.

Ejemplo

open class Empleado {
    private val id: Int = 1
    protected val nombre: String = "Juan"
    internal val departamento: String = "Ventas"
    val puesto: String = "Gerente"
}

Polimorfismo en Kotlin

El polimorfismo permite tratar objetos de diferentes clases de forma unificada basándose en una clase base común.

Sobrescritura de métodos

Para que un método pueda ser sobrescrito en una subclase, debe declararse con open. La sobrescritura se realiza con override.

open class Vehiculo {
    open fun conducir() {
        println("Conduciendo un vehículo")
    }
}

class Coche : Vehiculo() {
    override fun conducir() {
        println("Conduciendo un coche")
    }
}

Uso del polimorfismo

fun main() {
    val vehiculo: Vehiculo = Coche()
    vehiculo.conducir()  // Salida: Conduciendo un coche
}

En este ejemplo, aunque vehiculo se declara como tipo Vehiculo, en tiempo de ejecución se utiliza la implementación de Coche.

Encapsulación en Kotlin

La encapsulación consiste en ocultar los detalles internos de una clase y exponer solo lo necesario. Esto se logra mediante el uso de modificadores de visibilidad y propiedades con getters y setters personalizados.

Propiedades con acceso controlado

class CuentaBancaria {
    var saldo: Double = 0.0
        private set

    fun depositar(cantidad: Double) {
        if (cantidad > 0) {
            saldo += cantidad
        }
    }

    fun retirar(cantidad: Double) {
        if (cantidad > 0 && cantidad <= saldo) {
            saldo -= cantidad
        }
    }
}

En este ejemplo, el setter de saldo es privado; por lo tanto, solo puede modificarse a través de los métodos depositar y retirar.

Clases abstractas e interfaces

Kotlin soporta clases abstractas e interfaces para definir contratos que las clases concretas deben implementar.

Clases abstractas

Una clase abstracta no puede instanciarse y puede contener métodos abstractos y concretos.

abstract class Figura {
    abstract fun area(): Double
    fun describir() {
        println("Soy una figura geométrica")
    }
}

Interfaces

Las interfaces pueden contener métodos abstractos y métodos con implementación.

interface Dibujable {
    fun dibujar()
    fun mover(x: Int, y: Int) {
        println("Moviendo a coordenadas ($x, $y)")
    }
}

Implementación

class Circulo(val radio: Double) : Figura(), Dibujable {
    override fun area(): Double {
        return Math.PI * radio * radio
    }

    override fun dibujar() {
        println("Dibujando un círculo de radio $radio")
    }
}

Data classes

Las data classes son clases diseñadas para almacenar datos y proporcionan implementaciones automáticas de métodos comunes como toString(), equals() y hashCode().

Definición de una data class

data class Usuario(val id: Int, val nombre: String)

Uso

val usuario1 = Usuario(1, "María")
val usuario2 = Usuario(2, "Carlos")
println(usuario1)  // Salida: Usuario(id=1, nombre=María)

Clases anidadas e internas

Kotlin permite definir clases dentro de otras clases.

Clase anidada (nested class)

Por defecto, una clase definida dentro de otra es estática.

class Contenedor {
    class Anidada {
        fun mensaje() = "Dentro de la clase anidada"
    }
}

val mensaje = Contenedor.Anidada().mensaje()
println(mensaje)

Clase interna (inner class)

Para acceder a los miembros de la clase externa, se utiliza inner.

class Externa(val valor: Int) {
    inner class Interna {
        fun doble() = valor * 2
    }
}

val externa = Externa(5)
val resultado = externa.Interna().doble()
println(resultado)  // Salida: 10

Enumeraciones (enum classes)

Las enum classes representan un conjunto de constantes.

Definición y uso

enum class Direccion {
    NORTE, SUR, ESTE, OESTE
}

val direccion = Direccion.NORTE
println(direccion)

Objetos singleton

En Kotlin, puedes crear objetos singleton utilizando la palabra clave object.

Definición de un objeto singleton

object GestorConfiguracion {
    val configuraciones = mutableMapOf<String, String>()

    fun agregarConfiguracion(clave: String, valor: String) {
        configuraciones[clave] = valor
    }
}

Uso

GestorConfiguracion.agregarConfiguracion("tema", "oscuro")
println(GestorConfiguracion.configuraciones)
Empezar curso de Kotlin

Lecciones de este módulo de Kotlin

Lecciones de programación del módulo Programación Orientada a Objetos del curso de Kotlin.

Ejercicios de programación en este módulo de Kotlin

Evalúa tus conocimientos en Programación Orientada a Objetos con ejercicios de programación Programación Orientada a Objetos de tipo Test, Puzzle, Código y Proyecto con VSCode.