Herramientas de usuario

Herramientas del sitio


anexos:kotlin2

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

anexos:kotlin2 [2026/09/21 11:05] – creado fernandoanexos:kotlin2 [2026/09/21 13:14] (actual) fernando
Línea 1: Línea 1:
 ====== Programación Orientada a Objetos en Kotlin ====== ====== Programación Orientada a Objetos en Kotlin ======
 +
 +===== Clases =====
 +
 +La **Programación Orientada a Objetos (POO)** es un paradigma de programación basado en la utilización de **objetos**.
 +
 +Un objeto agrupa principalmente:
 +
 +  * **Datos**, representados mediante propiedades.
 +  * **Comportamiento**, representado mediante funciones.
 +
 +Por ejemplo, si queremos representar un coche podríamos tener:
 +
 +<code>
 +Coche
 +
 +Propiedades:
 +marca
 +modelo
 +velocidad
 +
 +Funciones:
 +acelerar()
 +frenar()
 +mostrarInformacion() </code>
 +
 +Para definir este tipo de objetos utilizaremos **clases**.
 +
 +==== Clases y objetos ====
 +
 +Una **clase** es una plantilla que define las características y el comportamiento que tendrán los objetos creados a partir de ella.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Coche {
 +    var marca = ""
 +    var modelo = ""
 +    var velocidad = 0
 +}
 +</code>
 +
 +Esta clase define cómo serán los objetos de tipo `Coche`.
 +
 +Sin embargo, declarar una clase **no crea ningún coche**.
 +
 +Para trabajar con ella debemos crear objetos o **instancias** de la clase.
 +
 +<WRAP info>
 +**Clase frente a objeto**
 +
 +Podemos entender una clase como un plano o plantilla y un objeto como una realización concreta de ese plano.
 +
 +Una misma clase puede utilizarse para crear muchos objetos diferentes. </WRAP>
 +
 +==== Declaración de una clase ====
 +
 +Para declarar una clase utilizamos la palabra reservada `class` seguida del nombre de la clase:
 +
 +<code kotlin>
 +class Persona {
 +
 +} </code>
 +
 +Por convenio, los nombres de las clases comienzan con mayúscula:
 +
 +<code kotlin>
 +class Persona
 +class Alumno
 +class Producto
 +class CuentaBancaria
 +</code>
 +
 +Una clase puede estar formada por dos partes:
 +
 +  * **Cabecera de la clase**.
 +  * **Cuerpo de la clase**.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Persona(val nombre: String) {
 +
 +```
 +var edad = 0
 +
 +fun saludar() {
 +    println("Hola, soy $nombre")
 +}
 +```
 +
 +} </code>
 +
 +La cabecera es:
 +
 +<code kotlin>
 +class Persona(val nombre: String)
 +</code>
 +
 +El cuerpo es todo lo que aparece entre `{ }`:
 +
 +<code kotlin>
 +{
 +    var edad = 0
 +
 +```
 +fun saludar() {
 +    println("Hola, soy $nombre")
 +}
 +```
 +
 +} </code>
 +
 +El cuerpo puede contener, entre otros elementos:
 +
 +  * Propiedades.
 +  * Funciones.
 +  * Bloques de inicialización.
 +  * Constructores secundarios.
 +  * Otras clases u objetos.
 +
 +Si una clase no necesita cuerpo podemos omitir las llaves:
 +
 +<code kotlin>
 +class Persona(val nombre: String, var edad: Int)
 +</code>
 +
 +==== Crear objetos ====
 +
 +Para crear un objeto escribimos el nombre de la clase seguido de paréntesis:
 +
 +<code kotlin>
 +val persona = Persona("Ana", 20)
 +</code>
 +
 +A esta operación la denominamos **instanciar una clase**.
 +
 +`persona` es ahora un objeto o instancia de `Persona`.
 +
 +<WRAP important>
 +A diferencia de otros lenguajes orientados a objetos como Java, en Kotlin **no se utiliza la palabra `new`** para crear objetos.
 +</WRAP>
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +// Kotlin
 +val persona = Persona("Ana", 20)
 +</code>
 +
 +No escribimos:
 +
 +<code kotlin>
 +// Incorrecto en Kotlin
 +// val persona = new Persona("Ana", 20)
 +</code>
 +
 +Podemos crear múltiples objetos de una misma clase:
 +
 +<code kotlin>
 +val persona1 = Persona("Ana", 20)
 +val persona2 = Persona("Carlos", 25)
 +val persona3 = Persona("Marta", 19)
 +</code>
 +
 +Todos son objetos diferentes, aunque hayan sido creados utilizando la misma clase.
 +
 +==== Propiedades ====
 +
 +Las variables que forman parte de una clase reciben normalmente el nombre de **propiedades**.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Coche {
 +    var marca = ""
 +    var modelo = ""
 +    var velocidad = 0
 +}
 +</code>
 +
 +La clase `Coche` tiene tres propiedades:
 +
 +<code>
 +marca
 +modelo
 +velocidad
 +</code>
 +
 +Podemos acceder a ellas utilizando el operador `.`:
 +
 +<code kotlin>
 +val coche = Coche()
 +
 +coche.marca = "Toyota"
 +coche.modelo = "Corolla"
 +coche.velocidad = 50
 +
 +println(coche.marca)
 +println(coche.modelo)
 +println(coche.velocidad) </code>
 +
 +==== Propiedades val y var ====
 +
 +Al igual que las variables normales, las propiedades pueden declararse utilizando `val` o `var`.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int
 +)
 +</code>
 +
 +En este caso:
 +
 +  * `nombre` es una propiedad que no puede reasignarse.
 +  * `edad` es una propiedad que puede modificarse.
 +
 +<code kotlin>
 +val persona = Persona("Ana", 20)
 +
 +persona.edad = 21 </code>
 +
 +Esto es correcto.
 +
 +Sin embargo:
 +
 +<code kotlin>
 +// ERROR
 +// persona.nombre = "Marta"
 +</code>
 +
 +no está permitido porque `nombre` fue declarado mediante `val`.
 +
 +<WRAP tip>
 +Al igual que con las variables normales, es recomendable utilizar `val` cuando una propiedad no necesite ser reasignada.
 +</WRAP>
 +
 +==== Funciones de una clase ====
 +
 +Las clases también pueden contener funciones.
 +
 +Estas funciones representan el **comportamiento de los objetos**.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Coche(
 +    val marca: String,
 +    val modelo: String,
 +    var velocidad: Int
 +) {
 +
 +```
 +fun acelerar() {
 +    velocidad += 10
 +}
 +
 +fun frenar() {
 +    velocidad -= 10
 +}
 +
 +fun mostrarInformacion() {
 +    println("$marca $modelo - $velocidad km/h")
 +}
 +```
 +
 +} </code>
 +
 +Podemos utilizar estas funciones mediante el operador `.`:
 +
 +<code kotlin>
 +fun main() {
 +
 +```
 +val coche = Coche(
 +    "Toyota",
 +    "Corolla",
 +    0
 +)
 +
 +coche.acelerar()
 +coche.acelerar()
 +
 +coche.mostrarInformacion()
 +```
 +
 +} </code>
 +
 +Resultado:
 +
 +<code>
 +Toyota Corolla - 20 km/h
 +</code>
 +
 +Las funciones pueden acceder directamente a las propiedades de su objeto:
 +
 +<code kotlin>
 +fun acelerar() {
 +    velocidad += 10
 +}
 +</code>
 +
 +No necesitamos pasar `velocidad` como parámetro porque pertenece al propio objeto.
 +
 +==== Constructor primario ====
 +
 +Cuando creamos un objeto normalmente queremos proporcionar sus datos iniciales.
 +
 +Para ello utilizamos un **constructor**.
 +
 +La forma más habitual en Kotlin es utilizar un **constructor primario**, que se declara directamente en la cabecera de la clase:
 +
 +<code kotlin>
 +class Persona(val nombre: String, var edad: Int) {
 +
 +} </code>
 +
 +Ahora debemos proporcionar esos valores al crear el objeto:
 +
 +<code kotlin>
 +val persona = Persona("Ana", 20)
 +</code>
 +
 +Podemos acceder posteriormente a ellos:
 +
 +<code kotlin>
 +println(persona.nombre)
 +println(persona.edad)
 +</code>
 +
 +Resultado:
 +
 +<code>
 +Ana
 +20
 +</code>
 +
 +La sintaxis completa del constructor podría escribirse utilizando la palabra `constructor`:
 +
 +<code kotlin>
 +class Persona constructor(val nombre: String, var edad: Int) {
 +
 +} </code>
 +
 +Sin embargo, normalmente podemos omitirla:
 +
 +<code kotlin>
 +class Persona(val nombre: String, var edad: Int) {
 +
 +} </code>
 +
 +Esta es la forma que utilizaremos habitualmente.
 +
 +==== Parámetros y propiedades del constructor ====
 +
 +Existe una diferencia importante entre escribir:
 +
 +<code kotlin>
 +class Persona(nombre: String)
 +</code>
 +
 +y:
 +
 +<code kotlin>
 +class Persona(val nombre: String)
 +</code>
 +
 +En el segundo caso `nombre` se convierte en una **propiedad del objeto**.
 +
 +Por ello podemos hacer:
 +
 +<code kotlin>
 +val persona = Persona("Ana")
 +
 +println(persona.nombre) </code>
 +
 +Sin `val` o `var`, el parámetro del constructor no se convierte automáticamente en una propiedad accesible del objeto.
 +
 +Por tanto:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int
 +)
 +</code>
 +
 +es una forma muy habitual de declarar simultáneamente:
 +
 +  * Los parámetros necesarios para crear el objeto.
 +  * Las propiedades que almacenará.
 +
 +==== Valores por defecto ====
 +
 +Los parámetros del constructor pueden tener valores por defecto:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String = "Desconocido",
 +    var edad: Int = 0
 +)
 +</code>
 +
 +Ahora podemos crear objetos de diferentes maneras:
 +
 +<code kotlin>
 +val persona1 = Persona()
 +
 +val persona2 = Persona("Ana")
 +
 +val persona3 = Persona("Carlos", 25) </code>
 +
 +Los valores serán:
 +
 +<code>
 +persona1 -> nombre = "Desconocido", edad = 0
 +
 +persona2 -> nombre = "Ana", edad = 0
 +
 +persona3 -> nombre = "Carlos", edad = 25 </code>
 +
 +También podemos utilizar argumentos con nombre:
 +
 +<code kotlin>
 +val persona = Persona(
 +    edad = 25,
 +    nombre = "Carlos"
 +)
 +</code>
 +
 +==== Propiedades adicionales ====
 +
 +No todas las propiedades tienen que declararse en el constructor.
 +
 +Podemos declarar otras propiedades dentro del cuerpo de la clase:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    val anioNacimiento: Int
 +) {
 +
 +```
 +val mayoriaEdad = 18
 +
 +var activo = true
 +```
 +
 +} </code>
 +
 +También podemos calcular una propiedad utilizando los datos recibidos por el constructor:
 +
 +<code kotlin>
 +class Rectangulo(
 +    val base: Double,
 +    val altura: Double
 +) {
 +
 +```
 +val area = base * altura
 +```
 +
 +} </code>
 +
 +Uso:
 +
 +<code kotlin>
 +val rectangulo = Rectangulo(10.0, 5.0)
 +
 +println(rectangulo.area) </code>
 +
 +Resultado:
 +
 +<code>
 +50.0
 +</code>
 +
 +==== Bloque init ====
 +
 +En ocasiones necesitamos ejecutar instrucciones cuando se crea un objeto.
 +
 +Para ello podemos utilizar un bloque `init`.
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int
 +) {
 +
 +```
 +init {
 +    println("Creando una nueva persona")
 +}
 +```
 +
 +} </code>
 +
 +Cuando hacemos:
 +
 +<code kotlin>
 +val persona = Persona("Ana", 20)
 +</code>
 +
 +se ejecutará automáticamente el bloque `init`.
 +
 +Podemos acceder desde él a los parámetros y propiedades:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int
 +) {
 +
 +```
 +init {
 +    println("Creando a $nombre")
 +    println("Edad inicial: $edad")
 +}
 +```
 +
 +} </code>
 +
 +==== Validación durante la inicialización ====
 +
 +Uno de los usos del bloque `init` es comprobar que los datos utilizados para crear un objeto sean correctos.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int
 +) {
 +
 +```
 +init {
 +    require(edad >= 0) {
 +        "La edad no puede ser negativa"
 +    }
 +}
 +```
 +
 +} </code>
 +
 +Esto sería válido:
 +
 +<code kotlin>
 +val persona = Persona("Ana", 20)
 +</code>
 +
 +Pero:
 +
 +<code kotlin>
 +val persona = Persona("Ana", -5)
 +</code>
 +
 +provocaría un error porque no se cumple la condición establecida mediante `require()`.
 +
 +Otro ejemplo:
 +
 +<code kotlin>
 +class Producto(
 +    val nombre: String,
 +    val precio: Double
 +) {
 +
 +```
 +init {
 +    require(nombre.isNotBlank()) {
 +        "El nombre no puede estar vacío"
 +    }
 +
 +    require(precio >= 0) {
 +        "El precio no puede ser negativo"
 +    }
 +}
 +```
 +
 +} </code>
 +
 +<WRAP info>
 +`require()` permite indicar una condición que debe cumplirse.
 +
 +Si la condición es falsa, Kotlin lanza una excepción indicando que el argumento proporcionado no es válido.
 +
 +No es necesario profundizar todavía en las excepciones para poder utilizar esta validación básica. </WRAP>
 +
 +==== Constructores secundarios ====
 +
 +Una clase puede tener además **constructores secundarios**.
 +
 +Se declaran mediante `constructor` dentro del cuerpo de la clase.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int
 +) {
 +
 +```
 +constructor(nombre: String) : this(nombre, 0)
 +```
 +
 +} </code>
 +
 +Ahora podemos crear:
 +
 +<code kotlin>
 +val persona1 = Persona("Ana", 20)
 +
 +val persona2 = Persona("Carlos") </code>
 +
 +En el segundo caso se utiliza el constructor secundario:
 +
 +<code kotlin>
 +constructor(nombre: String) : this(nombre, 0)
 +</code>
 +
 +que llama al constructor primario proporcionando:
 +
 +<code>
 +nombre -> valor recibido
 +edad   -> 0
 +</code>
 +
 +<WRAP important>
 +Si existe un constructor primario, los constructores secundarios deben terminar delegando en él mediante `this(...)`.
 +</WRAP>
 +
 +No obstante, Kotlin permite solucionar muchas situaciones mediante **valores por defecto**, por lo que no necesitaremos constructores secundarios con tanta frecuencia como en otros lenguajes.
 +
 +Por ejemplo, en el caso anterior probablemente sea más sencillo escribir:
 +
 +<code kotlin>
 +class Persona(
 +    val nombre: String,
 +    var edad: Int = 0
 +)
 +</code>
 +
 +===== Ejemplo completo: clase Producto =====
 +
 +Vamos a representar un producto de una tienda.
 +
 +Cada producto tendrá:
 +
 +  * Nombre.
 +  * Precio.
 +  * Stock.
 +
 +Además podrá:
 +
 +  * Mostrar su información.
 +  * Vender unidades.
 +  * Reponer unidades.
 +  * Calcular el valor total del stock.
 +
 +<code kotlin>
 +class Producto(
 +    val nombre: String,
 +    val precio: Double,
 +    var stock: Int = 0
 +) {
 +
 +```
 +init {
 +    require(nombre.isNotBlank()) {
 +        "El nombre no puede estar vacío"
 +    }
 +
 +    require(precio >= 0) {
 +        "El precio no puede ser negativo"
 +    }
 +
 +    require(stock >= 0) {
 +        "El stock no puede ser negativo"
 +    }
 +}
 +
 +fun vender(cantidad: Int) {
 +
 +    if (cantidad <= 0) {
 +        println("La cantidad debe ser positiva")
 +    } else if (cantidad > stock) {
 +        println("No existe stock suficiente")
 +    } else {
 +        stock -= cantidad
 +        println("Venta realizada")
 +    }
 +}
 +
 +fun reponer(cantidad: Int) {
 +
 +    if (cantidad > 0) {
 +        stock += cantidad
 +    }
 +}
 +
 +fun calcularValorStock(): Double {
 +    return precio * stock
 +}
 +
 +fun mostrarInformacion() {
 +    println("Producto: $nombre")
 +    println("Precio: $precio €")
 +    println("Stock: $stock")
 +    println("Valor del stock: ${calcularValorStock()} €")
 +}
 +```
 +
 +} </code>
 +
 +Podemos utilizarla:
 +
 +<code kotlin>
 +fun main() {
 +
 +```
 +val teclado = Producto(
 +    nombre = "Teclado mecánico",
 +    precio = 59.95,
 +    stock = 10
 +)
 +
 +teclado.mostrarInformacion()
 +
 +teclado.vender(3)
 +
 +teclado.reponer(5)
 +
 +teclado.mostrarInformacion()
 +```
 +
 +} </code>
 +
 +En este ejemplo podemos identificar claramente los dos elementos principales de un objeto.
 +
 +**Estado del objeto:**
 +
 +<code>
 +nombre
 +precio
 +stock
 +</code>
 +
 +**Comportamiento del objeto:**
 +
 +<code>
 +vender()
 +reponer()
 +calcularValorStock()
 +mostrarInformacion()
 +</code>
 +
 +===== Referencias entre objetos =====
 +
 +Una variable que contiene un objeto contiene una **referencia a ese objeto**.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +val producto1 = Producto(
 +    "Teclado",
 +    50.0,
 +    10
 +)
 +
 +val producto2 = producto1 </code>
 +
 +En este caso no hemos creado dos productos independientes.
 +
 +`producto1` y `producto2` hacen referencia al **mismo objeto**.
 +
 +Por tanto:
 +
 +<code kotlin>
 +producto2.stock = 5
 +
 +println(producto1.stock) </code>
 +
 +mostrará:
 +
 +<code>
 +5
 +</code>
 +
 +Esto es diferente de lo que ocurre con tipos sencillos como `Int`:
 +
 +<code kotlin>
 +var numero1 = 10
 +var numero2 = numero1
 +
 +numero2 = 20
 +
 +println(numero1) </code>
 +
 +El resultado continúa siendo:
 +
 +<code>
 +10
 +</code>
 +
 +<WRAP important>
 +Comprender que los objetos se manejan mediante referencias será importante posteriormente cuando trabajemos con objetos, colecciones y aplicaciones Android.
 +</WRAP>
 +
 +===== Ejercicio — Gestión de vehículos =====
 +
 +Crea una clase denominada:
 +
 +<code kotlin>
 +Vehiculo
 +</code>
 +
 +Cada vehículo debe almacenar:
 +
 +  * Matrícula.
 +  * Marca.
 +  * Modelo.
 +  * Kilómetros.
 +  * Nivel de combustible.
 +  * Capacidad máxima del depósito.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +val coche = Vehiculo(
 +    matricula = "1234 ABC",
 +    marca = "Toyota",
 +    modelo = "Corolla",
 +    kilometros = 50000,
 +    combustible = 30.0,
 +    capacidadDeposito = 50.0
 +)
 +</code>
 +
 +La clase debe proporcionar las siguientes funciones:
 +
 +<code kotlin>
 +mostrarInformacion()
 +
 +repostar(litros)
 +
 +realizarViaje(kilometros, consumo) </code>
 +
 +La función `repostar()` debe comprobar:
 +
 +  * Que la cantidad sea positiva.
 +  * Que el depósito no supere su capacidad máxima.
 +
 +Por ejemplo, si quedan 40 litros en un depósito de 50 y hacemos:
 +
 +<code kotlin>
 +coche.repostar(20.0)
 +</code>
 +
 +el depósito deberá quedar en:
 +
 +<code>
 +50 litros
 +</code>
 +
 +La función:
 +
 +<code kotlin>
 +realizarViaje(kilometros, consumo)
 +</code>
 +
 +recibirá:
 +
 +  * Distancia que se quiere recorrer.
 +  * Consumo del vehículo en litros cada 100 km.
 +
 +Por ejemplo:
 +
 +<code kotlin>
 +coche.realizarViaje(
 +    kilometros = 200,
 +    consumo = 6.5
 +)
 +</code>
 +
 +El programa deberá calcular el combustible necesario:
 +
 +<code>
 +combustibleNecesario = kilometros * consumo / 100
 +</code>
 +
 +Si existe combustible suficiente:
 +
 +  * Restar el combustible consumido.
 +  * Aumentar el kilometraje del vehículo.
 +  * Mostrar un mensaje indicando que el viaje se ha realizado.
 +
 +Si no existe combustible suficiente:
 +
 +  * No modificar el kilometraje.
 +  * No modificar el combustible.
 +  * Mostrar un mensaje indicando que no existe combustible suficiente.
 +
 +Además, utiliza un bloque `init` para comprobar que:
 +
 +  * Los kilómetros iniciales no sean negativos.
 +  * El combustible no sea negativo.
 +  * La capacidad del depósito sea mayor que cero.
 +
 +Finalmente, crea al menos **tres vehículos diferentes** y realiza distintas operaciones sobre ellos.
 +
 +===== Ejercicio de ampliación =====
 +
 +Crea una clase `Alumno` que almacene:
 +
 +<code>
 +nombre
 +apellidos
 +edad
 +notas
 +</code>
 +
 +Las notas pueden almacenarse en:
 +
 +<code kotlin>
 +val notas = mutableListOf<Double>()
 +</code>
 +
 +La clase deberá proporcionar las funciones:
 +
 +<code kotlin>
 +agregarNota(nota)
 +
 +calcularMedia()
 +
 +estaAprobado()
 +
 +mostrarInformacion() </code>
 +
 +`agregarNota()` solamente deberá aceptar valores entre `0` y `10`.
 +
 +`calcularMedia()` devolverá la media de las notas del alumno.
 +
 +`estaAprobado()` devolverá un `Boolean` indicando si la media es igual o superior a `5`.
 +
 +`mostrarInformacion()` deberá producir una salida similar a:
 +
 +<code>
 +Alumno: Ana García
 +Edad: 20
 +Notas: 7.5, 8.0, 6.5, 9.0
 +Media: 7.75
 +Resultado: APROBADO
 +</code>
 +
 +Crea posteriormente varios objetos `Alumno`, añade diferentes notas y muestra sus resultados.
 +
  
 ---- ----
  
 (c) {{date> %Y}} Fernando Valdeón (c) {{date> %Y}} Fernando Valdeón
anexos/kotlin2.1789981519.txt.gz · Última modificación: por fernando