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)ytakeLast(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().