Fundamentos de Kotlin y técnicas prácticas de uso

val mensaje: String? = "HolaMundo"
val nombre: String = obtenerNombre() ?: return  // Si es nulo, salir
println("$param1 + $param2 = ${param1 + param2}") // Plantillas de cadena
val SALUDO_PERMANENTE = "HolaChile"  // Inferencia de tipo automática
val numeros = arrayOf(10, 20, 30)
val intervalo: IntRange = 0..512  // [0, 512]
val intervaloAbierto: IntRange = 0 until 512 // [0, 512) = [0, 511]

class B {
    var contador = 0
    lateinit var descripcion: String  // Inicialización tardía para var
    lateinit var elemento: X
    val instanciaPesada: X by lazy {
        // Inicialización perezosa para val
        println("Creando X")
        X()
    }
    var posibleNulo: String? = null // Mejor evitar inicializaciones nulas
}

abstract class Empleado(open val edad: Int) {
    abstract fun realizarTarea()
}

// En lugar de operador ternario, usar expresión if
val mayor = if (a > b) a else b

// Clases de datos concisas
data class Pronostico(val fecha: Date, val temperatura: Float, val descripcion: String)

// Constructor primario y secundario
class Persona(var nombre: String) {
    private var detalle: String? = null
    init {
        nombre = "Laura"
    }
    internal fun saludar() {
        println("Hola $nombre")
    }
    constructor(nombre: String, detalle: String) : this(nombre) {
        this.detalle = detalle
    }
}

// Miembros estáticos con companion object
class Longitud private constructor(val valor: Double) {
    companion object {
        @JvmStatic
        fun desdeDouble(d: Double): Longitud = Longitud(d)
        @JvmField
        val ETIQUETA: String = "Longitud"
    }
}

// Implementación múltiple de interfaces
class Director : Conductor, Escritor {
    override fun manejar() {}
    override fun escribir() {}
}

Extensiones y expresiones avanzadas

Las funciones de extensión permiten añadir métodos a clases exitsentes sin heredar. Por ejemplo, un operador personalizado para repetir cadenas:

operator fun String.repetir(veces: Int): String {
    val sb = StringBuilder()
    for (i in 0 until veces) sb.append(this)
    return sb.toString()
}
// Uso: "abc".repetir(3)

Referencias a funciones como parámetros:

val impresora = ImpresoraPersonalizada()
args.forEach(impresora::imprimir)

class ImpresoraPersonalizada {
    fun imprimir(item: Any) {
        kotlin.io.println(item) // Desambiguación con el nombre completo
    }
}

Trabajo con colecciones y funciones de orden superior

val original = listOf(1, 2, 3, 5, 10, 8, 2)
// Transformación con map
val procesada = original.map { it * 2 + 3 }
// Filtrado con filter
val pares = original.filter { it % 2 == 0 }
// Extraer mientras se cumpla una condición
original.takeWhile { it <= 3 }.forEach(::println)

// Uso de flatMap para aplanar colecciones
val anidada = listOf(listOf(1,2), listOf(3,4))
val plana = anidada.flatMap { it } // [1,2,3,4]

Seguridad nula y operadores de llamada segura

Kotlin evita los NullPointerException mediante el sistema de tipos. El operador ?. ejecuta solo si no es nulo; !! fuerza la llamada pero puede lanzar excepción.

val persona: Persona? = buscarPersona()
// Llamada segura con let
persona?.let {
    it.realizarTarea()
    println(it.edad)
}
// Aplicación directa de múltiples operaciones
persona?.apply {
    realizarTarea()
    println(edad)
}

Elvis operator ?: simplifica comprobaciones de nulidad:

val identificador = persona.id ?: return
// Equivale a: if (persona.id == null) return else id

Inicializadores y delegados

Además de lazy, existen otros delegados de propiedades y de clase. La delegación de clase evita la herencia excesiva:

interface Base { fun imprimir() }
class BaseImpl(val x: Int) : Base {
    override fun imprimir() = println(x)
}
class Derivada(b: Base) : Base by b

// Delegación de interfaces con "by"
class GerenteSenior(
    val conductor: Conductor,
    val escritor: Escritor
) : Conductor by conductor, Escritor by escritor

Inicialización de objetos con apply

val vista = TextView(contexto).apply {
    texto = "Bienvenido"
    setOnClickListener { /* manejar clic */ }
}

Simplificación de recursos con use

BufferedReader(FileReader("datos.txt")).use { reader ->
    var linea: String?
    while (reader.readLine().also { linea = it } != null) {
        println(linea)
    }
}

Definición de singleton y miembros estáticos

object ManejadorConfiguracion {
    val nombreApp = "MiApp"
    fun iniciar() { println("Iniciando") }
}

class Medidor private constructor(val valor: Double) {
    companion object {
        @JvmStatic fun desde(cm: Double) = Medidor(cm)
        const val UNIDAD = "m"
    }
}

Comprobaciones de igualdad

== compara contenido (equivale a equals), === compara referencia de memoria.

Genéricos con varianza

var listaVistas: List<out View>   // covarianza
var listaWidgets: List<in TextView>  // contravarianza

Funciones estándar: let, apply, run, also, with

Regla rápida: si se necesita devolver el propio objeto usar apply (this) o also (it). Si se necesita otro resultado, run (this) o let (it).

Corrutinas

Las corrutinas proporcionan concurrencia estructurada en Kotlin, siendo una alternativa ligera a los hilos tradicionales.

Atajos prácticos

  • repeat(10) { ... } para bucles simples.
  • take(n) y takeLast(n) para obtener subconjuntos.
  • sortedBy { it.atributo } para ordenar.
  • slice(0..2) para extraer porciones.
  • mapOf("clave" to valor) para crear mapas.
  • Funciones de extensión para añadir utilidades (como Context.toast).
  • Propiedades de nivel superior para utilerías sin clase contenedora.

Recordar: las colecciones por defecto son de solo lectura (listOf), usar mutableListOf para mutabilidad; converitr con toMutableList().

Etiquetas: Kotlin sintaxis-kotlin seguridad-nula extensiones Delegados

Publicado el 10-17 18:14