La Programación Orientada a Objetos (POO) es un paradigma de programación basado en la utilización de objetos.
Un objeto agrupa principalmente:
Por ejemplo, si queremos representar un coche podríamos tener:
Coche Propiedades: marca modelo velocidad Funciones: acelerar() frenar() mostrarInformacion()
Para definir este tipo de objetos utilizaremos clases.
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:
class Coche { var marca = "" var modelo = "" var velocidad = 0 }
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.
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.
Para declarar una clase utilizamos la palabra reservada `class` seguida del nombre de la clase:
class Persona { }
Por convenio, los nombres de las clases comienzan con mayúscula:
class Persona class Alumno class Producto class CuentaBancaria
Una clase puede estar formada por dos partes:
Por ejemplo:
class Persona(val nombre: String) { ``` var edad = 0 fun saludar() { println("Hola, soy $nombre") } ``` }
La cabecera es:
class Persona(val nombre: String)
El cuerpo es todo lo que aparece entre `{ }`:
{ var edad = 0 ``` fun saludar() { println("Hola, soy $nombre") } ``` }
El cuerpo puede contener, entre otros elementos:
Si una clase no necesita cuerpo podemos omitir las llaves:
class Persona(val nombre: String, var edad: Int)
Para crear un objeto escribimos el nombre de la clase seguido de paréntesis:
val persona = Persona("Ana", 20)
A esta operación la denominamos instanciar una clase.
`persona` es ahora un objeto o instancia de `Persona`.
A diferencia de otros lenguajes orientados a objetos como Java, en Kotlin no se utiliza la palabra `new` para crear objetos.
Por ejemplo:
// Kotlin val persona = Persona("Ana", 20)
No escribimos:
// Incorrecto en Kotlin // val persona = new Persona("Ana", 20)
Podemos crear múltiples objetos de una misma clase:
val persona1 = Persona("Ana", 20) val persona2 = Persona("Carlos", 25) val persona3 = Persona("Marta", 19)
Todos son objetos diferentes, aunque hayan sido creados utilizando la misma clase.
Las variables que forman parte de una clase reciben normalmente el nombre de propiedades.
Por ejemplo:
class Coche { var marca = "" var modelo = "" var velocidad = 0 }
La clase `Coche` tiene tres propiedades:
marca modelo velocidad
Podemos acceder a ellas utilizando el operador `.`:
val coche = Coche() coche.marca = "Toyota" coche.modelo = "Corolla" coche.velocidad = 50 println(coche.marca) println(coche.modelo) println(coche.velocidad)
Al igual que las variables normales, las propiedades pueden declararse utilizando `val` o `var`.
Por ejemplo:
class Persona( val nombre: String, var edad: Int )
En este caso:
val persona = Persona("Ana", 20) persona.edad = 21
Esto es correcto.
Sin embargo:
// ERROR // persona.nombre = "Marta"
no está permitido porque `nombre` fue declarado mediante `val`.
Al igual que con las variables normales, es recomendable utilizar `val` cuando una propiedad no necesite ser reasignada.
Las clases también pueden contener funciones.
Estas funciones representan el comportamiento de los objetos.
Por ejemplo:
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") } ``` }
Podemos utilizar estas funciones mediante el operador `.`:
fun main() { ``` val coche = Coche( "Toyota", "Corolla", 0 ) coche.acelerar() coche.acelerar() coche.mostrarInformacion() ``` }
Resultado:
Toyota Corolla - 20 km/h
Las funciones pueden acceder directamente a las propiedades de su objeto:
fun acelerar() { velocidad += 10 }
No necesitamos pasar `velocidad` como parámetro porque pertenece al propio objeto.
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:
class Persona(val nombre: String, var edad: Int) { }
Ahora debemos proporcionar esos valores al crear el objeto:
val persona = Persona("Ana", 20)
Podemos acceder posteriormente a ellos:
println(persona.nombre) println(persona.edad)
Resultado:
Ana 20
La sintaxis completa del constructor podría escribirse utilizando la palabra `constructor`:
class Persona constructor(val nombre: String, var edad: Int) { }
Sin embargo, normalmente podemos omitirla:
class Persona(val nombre: String, var edad: Int) { }
Esta es la forma que utilizaremos habitualmente.
Existe una diferencia importante entre escribir:
class Persona(nombre: String)
y:
class Persona(val nombre: String)
En el segundo caso `nombre` se convierte en una propiedad del objeto.
Por ello podemos hacer:
val persona = Persona("Ana") println(persona.nombre)
Sin `val` o `var`, el parámetro del constructor no se convierte automáticamente en una propiedad accesible del objeto.
Por tanto:
class Persona( val nombre: String, var edad: Int )
es una forma muy habitual de declarar simultáneamente:
Los parámetros del constructor pueden tener valores por defecto:
class Persona( val nombre: String = "Desconocido", var edad: Int = 0 )
Ahora podemos crear objetos de diferentes maneras:
val persona1 = Persona() val persona2 = Persona("Ana") val persona3 = Persona("Carlos", 25)
Los valores serán:
persona1 -> nombre = "Desconocido", edad = 0 persona2 -> nombre = "Ana", edad = 0 persona3 -> nombre = "Carlos", edad = 25
También podemos utilizar argumentos con nombre:
val persona = Persona( edad = 25, nombre = "Carlos" )
No todas las propiedades tienen que declararse en el constructor.
Podemos declarar otras propiedades dentro del cuerpo de la clase:
class Persona( val nombre: String, val anioNacimiento: Int ) { ``` val mayoriaEdad = 18 var activo = true ``` }
También podemos calcular una propiedad utilizando los datos recibidos por el constructor:
class Rectangulo( val base: Double, val altura: Double ) { ``` val area = base * altura ``` }
Uso:
val rectangulo = Rectangulo(10.0, 5.0) println(rectangulo.area)
Resultado:
50.0
En ocasiones necesitamos ejecutar instrucciones cuando se crea un objeto.
Para ello podemos utilizar un bloque `init`.
class Persona( val nombre: String, var edad: Int ) { ``` init { println("Creando una nueva persona") } ``` }
Cuando hacemos:
val persona = Persona("Ana", 20)
se ejecutará automáticamente el bloque `init`.
Podemos acceder desde él a los parámetros y propiedades:
class Persona( val nombre: String, var edad: Int ) { ``` init { println("Creando a $nombre") println("Edad inicial: $edad") } ``` }
Uno de los usos del bloque `init` es comprobar que los datos utilizados para crear un objeto sean correctos.
Por ejemplo:
class Persona( val nombre: String, var edad: Int ) { ``` init { require(edad >= 0) { "La edad no puede ser negativa" } } ``` }
Esto sería válido:
val persona = Persona("Ana", 20)
Pero:
val persona = Persona("Ana", -5)
provocaría un error porque no se cumple la condición establecida mediante `require()`.
Otro ejemplo:
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" } } ``` }
`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.
Una clase puede tener además constructores secundarios.
Se declaran mediante `constructor` dentro del cuerpo de la clase.
Por ejemplo:
class Persona( val nombre: String, var edad: Int ) { ``` constructor(nombre: String) : this(nombre, 0) ``` }
Ahora podemos crear:
val persona1 = Persona("Ana", 20) val persona2 = Persona("Carlos")
En el segundo caso se utiliza el constructor secundario:
constructor(nombre: String) : this(nombre, 0)
que llama al constructor primario proporcionando:
nombre -> valor recibido edad -> 0
Si existe un constructor primario, los constructores secundarios deben terminar delegando en él mediante `this(…)`.
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:
class Persona( val nombre: String, var edad: Int = 0 )
Vamos a representar un producto de una tienda.
Cada producto tendrá:
Además podrá:
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()} €") } ``` }
Podemos utilizarla:
fun main() { ``` val teclado = Producto( nombre = "Teclado mecánico", precio = 59.95, stock = 10 ) teclado.mostrarInformacion() teclado.vender(3) teclado.reponer(5) teclado.mostrarInformacion() ``` }
En este ejemplo podemos identificar claramente los dos elementos principales de un objeto.
Estado del objeto:
nombre precio stock
Comportamiento del objeto:
vender() reponer() calcularValorStock() mostrarInformacion()
Una variable que contiene un objeto contiene una referencia a ese objeto.
Por ejemplo:
val producto1 = Producto( "Teclado", 50.0, 10 ) val producto2 = producto1
En este caso no hemos creado dos productos independientes.
`producto1` y `producto2` hacen referencia al mismo objeto.
Por tanto:
producto2.stock = 5 println(producto1.stock)
mostrará:
5
Esto es diferente de lo que ocurre con tipos sencillos como `Int`:
var numero1 = 10 var numero2 = numero1 numero2 = 20 println(numero1)
El resultado continúa siendo:
10
Comprender que los objetos se manejan mediante referencias será importante posteriormente cuando trabajemos con objetos, colecciones y aplicaciones Android.
Crea una clase denominada:
Vehiculo
Cada vehículo debe almacenar:
Por ejemplo:
val coche = Vehiculo( matricula = "1234 ABC", marca = "Toyota", modelo = "Corolla", kilometros = 50000, combustible = 30.0, capacidadDeposito = 50.0 )
La clase debe proporcionar las siguientes funciones:
mostrarInformacion() repostar(litros) realizarViaje(kilometros, consumo)
La función `repostar()` debe comprobar:
Por ejemplo, si quedan 40 litros en un depósito de 50 y hacemos:
coche.repostar(20.0)
el depósito deberá quedar en:
50 litros
La función:
realizarViaje(kilometros, consumo)
recibirá:
Por ejemplo:
coche.realizarViaje( kilometros = 200, consumo = 6.5 )
El programa deberá calcular el combustible necesario:
combustibleNecesario = kilometros * consumo / 100
Si existe combustible suficiente:
Si no existe combustible suficiente:
Además, utiliza un bloque `init` para comprobar que:
Finalmente, crea al menos tres vehículos diferentes y realiza distintas operaciones sobre ellos.
Crea una clase `Alumno` que almacene:
nombre apellidos edad notas
Las notas pueden almacenarse en:
val notas = mutableListOf<Double>()
La clase deberá proporcionar las funciones:
agregarNota(nota) calcularMedia() estaAprobado() mostrarInformacion()
`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:
Alumno: Ana García Edad: 20 Notas: 7.5, 8.0, 6.5, 9.0 Media: 7.75 Resultado: APROBADO
Crea posteriormente varios objetos `Alumno`, añade diferentes notas y muestra sus resultados.
© 2026 Fernando Valdeón