Kotlin
Tutorial Kotlin: Clases y objetos
Aprende a definir clases y objetos en Kotlin: propiedades, métodos, constructores y más. Optimiza tu programación con conceptos de orientación a objetos.
Aprende Kotlin GRATIS y certifícateDefinición de clases y creación de objetos
En Kotlin, una clase es una estructura que permite crear nuevos tipos de datos agrupando propiedades y funciones relacionadas. Definir una clase es esencial para representar objetos del mundo real y establecer su comportamiento dentro del programa.
Para definir una clase básica en Kotlin, se utiliza la palabra clave class
seguida del nombre de la clase. Por ejemplo:
class Persona
Este fragmento de código declara una clase llamada Persona
sin propiedades ni funciones.
Para crear un objeto o instancia de una clase, se utiliza el operador ()
después del nombre de la clase. Por ejemplo:
val persona = Persona()
Aquí, persona
es un objeto de tipo Persona
, también se puede decir que persona
es una instancia de la clase Persona
.
Las clases también pueden tener un cuerpo entre llaves {}
donde se declaran propiedades y funciones adicionales. Si no hay necesidad de propiedades o funciones extra, las llaves pueden omitirse.
En resumen, la definición de clases y la creación de objetos en Kotlin es sencilla y concisa, facilitando una programación orientada a objetos efectiva y moderna.
Propiedades y métodos de las clases
En Kotlin, las propiedades y los métodos son elementos esenciales que definen el estado y el comportamiento de una clase. Las propiedades representan los datos o atributos que posee un objeto, mientras que los métodos son funciones que describen las acciones que este puede realizar.
Para declarar una propiedad dentro de una clase, se utilizan las palabras clave var
o val
, según se requiera que la propiedad sea mutable o inmutable. Por ejemplo:
class Persona {
var nombre: String = "Desconocido"
val edad: Int = 0
}
En este ejemplo, nombre
es una propiedad mutable cuyo valor puede cambiarse después de la inicialización, mientras que edad
es una propiedad inmutable que no puede modificarse una vez asignada.
Las propiedades en Kotlin vienen acompañadas de getters y setters generados automáticamente. El getter permite acceder al valor de la propiedad y el setter permite asignarle un nuevo valor (solo para propiedades mutables). Es posible personalizar estos accesores si se necesita añadir lógica adicional:
class Empleado {
var salario: Double = 0.0
set(value) {
field = if (value >= 0) value else 0.0
}
}
Aquí, el setter de salario
verifica que el valor asignado no sea negativo, asegurando que la propiedad mantenga un estado válido.
Además de las propiedades, las clases pueden tener métodos que definen su comportamiento. Un método es una función declarada dentro de la clase y puede acceder a las propiedades y otros métodos de la misma:
class Circulo(val radio: Double) {
fun calcularArea(): Double {
return Math.PI * radio * radio
}
}
El método calcularArea
utiliza la propiedad radio
para calcular y devolver el área del círculo. Los métodos permiten encapsular operaciones relacionadas con la clase y sus propiedades.
Es importante considerar los modificadores de visibilidad al declarar propiedades y métodos. Estos determinan desde dónde pueden ser accedidos:
public
: Accesible desde cualquier parte del código (es el valor por defecto).private
: Solo accesible dentro de la clase donde se declara.protected
: Accesible dentro de la clase y sus subclases.internal
: Accesible dentro del mismo módulo.
Por ejemplo:
class CuentaBancaria {
private var saldo: Double = 0.0
fun depositar(cantidad: Double) {
saldo += cantidad
}
fun obtenerSaldo(): Double {
return saldo
}
}
En este caso, la propiedad saldo
es privada, lo que significa que no puede ser accedida directamente desde fuera de la clase CuentaBancaria
, promoviendo el encapsulamiento de los datos.
Las propiedades también pueden ser calculadas, es decir, su valor se define mediante una expresión en lugar de almacenarse en memoria:
class Rectangulo(val ancho: Double, val alto: Double) {
val area: Double
get() = ancho * alto
}
La propiedad area
es calculada cada vez que se accede a ella, utilizando los valores de ancho
y alto
.
El método millasAKilometros
puede ser llamado sin crear una instancia de Conversor
, usando Conversor.millasAKilometros(10.0)
.
Además, Kotlin permite el uso de propiedades delegadas, donde el comportamiento de una propiedad es delegado a otra clase. Un ejemplo común es la delegación lazy
, que retrasa la inicialización de una propiedad hasta su primer acceso:
class DatosUsuario {
val perfilCompleto: Perfil by lazy {
cargarPerfilDesdeBaseDeDatos()
}
}
Con lazy
, la propiedad perfilCompleto
no se inicializa hasta que se utiliza por primera vez, optimizando así los recursos.
Por último, es posible sobrescribir propiedades y métodos en clases derivadas utilizando la palabra clave open
en la clase base y override
en la clase hija:
open class Animal {
open fun hacerSonido() {
println("El animal hace un sonido")
}
}
class Perro : Animal() {
override fun hacerSonido() {
println("El perro ladra")
}
}
El método hacerSonido
de la clase Perro
sobrescribe al de la clase Animal
, proporcionando una implementación específica.
Comprender y utilizar adecuadamente las propiedades y métodos es fundamental para aprovechar al máximo las capacidades de Kotlin en programación orientada a objetos, permitiendo crear clases robustas y funcionales.
Inicialización de objetos y constructores
La inicialización de objetos en Kotlin se realiza a través de constructores, que son funciones especiales destinadas a crear instancias de una clase. Kotlin ofrece una sintaxis concisa para definir constructores, permitiendo establecer propiedades y ejecutar código durante la inicialización.
En Kotlin, las clases pueden tener un constructor primario y uno o más constructores secundarios. El constructor primario forma parte de la definición de la clase y puede recibir parámetros que inicialicen las propiedades:
class Persona(val nombre: String, var edad: Int)
En este ejemplo, Persona
tiene un constructor primario con dos parámetros: nombre
y edad
, que se asignan directamente como propiedades de la clase.
Si se necesita ejecutar código adicional durante la inicialización, se pueden utilizar bloques de inicialización con la palabra clave init
:
class Persona(val nombre: String) {
val saludo: String
init {
saludo = "Hola, mi nombre es $nombre"
}
}
El bloque init
se ejecuta inmediatamente después del constructor primario y permite ejecutar código que no sea directamente la asignación de propiedades.
Además del constructor primario, las clases pueden definir constructores secundarios usando la palabra clave constructor
:
class Persona {
var nombre: String
var edad: Int
constructor(nombre: String) {
this.nombre = nombre
this.edad = 0
}
constructor(nombre: String, edad: Int) {
this.nombre = nombre
this.edad = edad
}
}
En este caso, Persona
tiene dos constructores secundarios que ofrecen diferentes formas de inicializar una instancia. Es importante destacar que si hay un constructor primario, los constructores secundarios deben delegar en él utilizando this
:
class Persona(val nombre: String) {
var edad: Int = 0
constructor(nombre: String, edad: Int) : this(nombre) {
this.edad = edad
}
}
Aquí, el constructor secundario constructor(nombre: String, edad: Int)
delega en el constructor primario this(nombre)
y luego inicializa edad
.
Las propiedades pueden tener valores predeterminados, lo que simplifica la inicialización:
class Persona(val nombre: String, var edad: Int = 0)
De esta manera, se puede crear una instancia sin especificar todos los parámetros:
val persona = Persona("Ana")
La propiedad edad
tomará el valor predeterminado 0
si no se proporciona.
Para propiedades que no pueden inicializarse en el constructor, Kotlin ofrece la palabra clave lateinit
para propiedades de inicialización tardía:
class Empleado {
lateinit var departamento: String
fun asignarDepartamento(dep: String) {
departamento = dep
}
fun imprimirDepartamento() {
if (::departamento.isInitialized) {
println("Departamento: $departamento")
} else {
println("Departamento no asignado")
}
}
}
Es importante comprobar si la propiedad departamento
ha sido inicializada antes de usarla, utilizando ::departamento.isInitialized
.
Otra opción es utilizar propiedades perezosas con by lazy
, que se inicializan en el primer acceso:
class Configuracion {
val configuracionCompleja: String by lazy {
cargarConfiguracion()
}
private fun cargarConfiguracion(): String {
// Código para cargar la configuración
return "Configuración cargada"
}
}
La inicialización perezosa es útil para retrasar operaciones costosas hasta que sean necesarias.
En clases que heredan de otras, es posible inicializar la clase base directamente desde el constructor primario:
open class Vehiculo(val marca: String)
class Coche(marca: String, val modelo: String) : Vehiculo(marca)
La clase Coche
llama al constructor de Vehiculo
pasando marca
como argumento, asegurando la inicialización correcta de la clase base.
Los bloques de inicialización y los constructores se ejecutan en un orden específico: primero se llama al constructor primario de la clase base, luego se ejecutan los bloques init
de la clase derivada y, finalmente, los constructores secundarios.
Es posible usar expresiones de inicialización directamente en las propiedades:
class Rectangulo(val ancho: Double, val alto: Double) {
val area = ancho * alto
}
Esta sintaxis permite inicializar propiedades sin necesidad de bloques init
o constructores adicionales.
En resumen, Kotlin proporciona mecanismos flexibles para la inicialización de objetos a través de constructores primarios y secundarios, bloques init
, propiedades con valores predeterminados y técnicas de inicialización tardía o perezosa. Comprender estos conceptos es esencial para crear clases robustas y garantizar que los objetos se instancien correctamente.
Clases anidadas e internas
En Kotlin, es posible definir clases anidadas dentro de otras clases. Una clase anidada es una clase declarada dentro del cuerpo de otra clase y, por defecto, es estática, lo que significa que no mantiene una referencia a la instancia de la clase externa.
Para crear una clase anidada, simplemente se declara una clase dentro de otra:
class Contenedor {
class Anidada {
fun mostrarMensaje() = "Este es un mensaje desde la clase anidada."
}
}
Para instanciar una clase anidada, se accede a ella a través del nombre de la clase externa:
fun main() {
val instancia = Contenedor.Anidada()
println(instancia.mostrarMensaje())
}
La salida de este programa será:
Este es un mensaje desde la clase anidada.
Al ser estática, la clase anidada no puede acceder directamente a los miembros de la clase externa. Si se necesita que la clase interna acceda a los miembros de la clase externa, se debe definir como una clase interna utilizando la palabra clave inner
:
class Contenedor {
private val mensaje = "Hola desde la clase externa."
inner class Interna {
fun mostrarMensaje() = "Mensaje: $mensaje"
}
}
En este caso, la clase Interna
es una clase interna que puede acceder a la propiedad mensaje
de la clase Contenedor
. Para crear una instancia de una clase interna, es necesario tener una instancia de la clase externa:
fun main() {
val contenedor = Contenedor()
val interna = contenedor.Interna()
println(interna.mostrarMensaje())
}
La salida será:
Mensaje: Hola desde la clase externa.
La palabra clave inner
es esencial para que la clase interna tenga acceso a los miembros de la clase externa. Sin inner
, la clase sería anidada y no podría interactuar con la instancia de la clase que la contiene.
Dentro de una clase interna, es posible referirse a la instancia de la clase externa utilizando this@NombreClase
:
class Contenedor(val valor: Int) {
inner class Interna {
fun duplicarValor() = this@Contenedor.valor * 2
}
}
Aquí, this@Contenedor
se refiere a la instancia de Contenedor
, permitiendo acceder a su propiedad valor
.
Las clases anidadas e internas son útiles para organizar el código y representar relaciones lógicas. Por ejemplo, si una clase solo tiene sentido en el contexto de otra, anidarla mejora la legibilidad y mantiene el código agrupado.
Es importante considerar que las clases internas mantienen una referencia a la instancia de la clase externa, lo que puede implicar consideraciones de rendimiento y gestión de memoria. Por ello, se recomienda utilizar clases internas únicamente cuando sea necesario acceder a los miembros de la clase externa.
Las clases anidadas pueden contener miembros estáticos. Si se necesita definir constantes o funciones relacionadas con la clase externa pero que no requieren una instancia de ella, se pueden utilizar objetos acompañantes (companion object
):
class Matematica {
class Constantes {
companion object {
const val PI = 3.1416
const val E = 2.7182
}
}
}
Para acceder a estas constantes, se utiliza el nombre completo:
fun main() {
println("El valor de PI es ${Matematica.Constantes.PI}")
}
Las clases internas pueden acceder a todos los miembros de la clase externa, incluso si son privados:
class Banco {
private val tasaInteres = 0.05
inner class Cuenta {
fun calcularInteres(monto: Double) = monto * tasaInteres
}
}
La clase Cuenta
puede utilizar tasaInteres
, a pesar de que es una propiedad privada de Banco
.
También es posible anidar clases dentro de funciones locales. Estas se llaman clases locales y su ámbito está limitado a la función donde se declaran:
fun operacion() {
class Suma(val a: Int, val b: Int) {
fun resultado() = a + b
}
val suma = Suma(5, 3)
println("El resultado es ${suma.resultado()}")
}
Las clases locales pueden ser útiles para encapsular lógica específica dentro de una función sin exponerla al resto del programa.
En resumen, las clases anidadas e internas proporcionan herramientas poderosas para estructurar y organizar el código en Kotlin. Las clases anidadas permiten agrupar clases relacionadas sin necesidad de acceder a los miembros de la clase externa, mientras que las clases internas facilitan la interacción directa con la instancia de la clase que las contiene.
Ejercicios de esta lección Clases y objetos
Evalúa tus conocimientos de esta lección Clases y objetos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Clases genéricas con varianza y restricciones
Introducción a las corutinas
Uso de asincronía con suspend, async y await
Formateo de cadenas texto
Uso de monads y manejo funcional de errores
Declaración y uso de variables y constantes
Uso de la concurrencia funcional con corutinas
Operaciones en colecciones
Uso de clases y objetos en Kotlin
Evaluación Kotlin
Funciones de orden superior y expresiones lambda en Kotlin
Herencia y polimorfismo en Kotlin
Inmutabilidad y datos inmutables
Uso de funciones parciales y currificaciones
Primer programa en Kotlin
Introducción a la programación funcional
Introducción a Kotlin
Uso de operadores y expresiones
Sistema de inventario de tienda
Uso de data classes y destructuring
Composición de funciones
Uso de interfaces y clases abstractas
Simulador de conversión de monedas
Programación funcional y concurrencia
Creación y uso de listas, conjuntos y mapas
Transformación en monads y functors
Crear e invocar funciones
Uso de las estructuras de control
Todas las lecciones de Kotlin
Accede a todas las lecciones de Kotlin y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Kotlin
Introducción Y Entorno
Instalación Y Primer Programa De Kotlin
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Clases Y Objetos
Programación Orientada A Objetos
Herencia Y Polimorfismo
Programación Orientada A Objetos
Interfaces Y Clases Abstractas
Programación Orientada A Objetos
Data Classes Y Destructuring
Programación Orientada A Objetos
Tipos Genéricos Y Varianza
Programación Orientada A Objetos
Listas, Conjuntos Y Mapas
Estructuras De Datos
Introducción A La Programación Funcional
Programación Funcional
Funciones De Primera Clase Y De Orden Superior
Programación Funcional
Inmutabilidad Y Datos Inmutables
Programación Funcional
Composición De Funciones
Programación Funcional
Monads Y Manejo Funcional De Errores
Programación Funcional
Operaciones Funcionales En Colecciones
Programación Funcional
Transformaciones En Monads Y Functors
Programación Funcional
Funciones Parciales Y Currificación
Programación Funcional
Introducción A Las Corutinas
Coroutines Y Asincronía
Asincronía Con Suspend, Async Y Await
Coroutines Y Asincronía
Concurrencia Funcional
Coroutines Y Asincronía
Evaluación
Evaluación
Certificados de superación de Kotlin
Supera todos los ejercicios de programación del curso de Kotlin y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la definición y creación de clases y objetos en Kotlin.
- Manejar propiedades y métodos, incluyendo getters y setters personalizados.
- Aplicar modificadores de visibilidad para el encapsulamiento.
- Utilizar constructores primarios y secundarios eficazmente.
- Implementar clases anidadas e internas, entendiendo su contexto y uso.
- Emplear inicialización perezosa y tardía de propiedades.