Seguridad Nula
## Objetivos de Aprendizaje
- Comprender el sistema de seguridad nula de Kotlin
- Usar el operador de llamada segura (?.)
- Trabajar con el operador Elvis (?:)
- Usar la aserción no-nula (!!)
- Aprender sobre casts seguros (as?)
## Tipos Anulables
El sistema de tipos de Kotlin distingue entre tipos anulables y no anulables:
```kotlin
fun main() {
// Non-nullable - cannot hold null
val name: String = "Kotlin"
// name = null // Error!
// Nullable - can hold null
var nullableName: String? = "Kotlin"
nullableName = null // OK
println(nullableName)
}
```
## Operador de Llamada Segura (?.)
El operador de llamada segura retorna null si el objeto es null:
```kotlin
fun main() {
val str: String? = "Hello"
// Safe call - returns length or null
val length1 = str?.length
println("Length 1: $length1") // 5
str = null
val length2 = str?.length
println("Length 2: $length2") // null
}
```
### Encadenando Llamadas Seguras
```kotlin
fun main() {
val person: Person? = Person(Address("New York"))
// Chained safe calls
val city = person?.address?.city
println("City: $city") // New York (or null if any part is null)
person?.address = null
val city2 = person?.address?.city
println("City 2: $city2") // null
}
class Person(var address: Address?)
class Address(var city: String?)
```
## Operador Elvis (?:)
El operador Elvis proporciona un valor por defecto cuando es null:
```kotlin
fun main() {
val str: String? = null
// Without Elvis - returns null
val length1 = str?.length
println("Length: $length1") // null
// With Elvis - returns default
val length2 = str?.length ?: 0
println("Length: $length2") // 0
}
```
### Elvis con throw
```kotlin
fun main() {
val str: String? = null
// Elvis can throw exceptions too
val nonNull = str ?: throw IllegalArgumentException("String cannot be null")
}
fun getName(person: Person?): String {
return person?.name ?: "Unknown"
}
```
## Aserción No-Nula (!!)
Usa `!!` cuando estás seguro de que un valor no es null:
```kotlin
fun main() {
val str: String? = "Hello"
// !! asserts value is not null
val length = str!!.length
println("Length: $length") // 5
// If null, throws NullPointerException
val nullStr: String? = null
// nullStr!!.length // Throws NullPointerException
}
```
### Cuándo Usarlo
- Solo cuando estás absolutamente seguro de que el valor no es null
- Evita usar `!!` en código de producción
- Elimina el propósito de la seguridad nula de Kotlin
## Cast Seguro (as?)
El cast seguro retorna null en lugar de lanzar ClassCastException:
```kotlin
fun main() {
val obj: Any = "Hello"
// Regular cast - throws if fails
// val str: String = obj as String
// Safe cast - returns null if fails
val str: String? = obj as? String
println("Safe cast: $str") // Hello
val num: Int? = obj as? Int
println("Safe cast to Int: $num") // null
}
```
## Función Let
Usa `let` para ejecutar código solo si el valor no es null:
```kotlin
fun main() {
val str: String? = "Hello"
// execute only if not null
str?.let {
println("Length: ${it.length}")
}
// with default behavior
str?.let { it.length } ?: println("String is null")
}
```
### Let con Múltiples Verificaciones de Null
```kotlin
fun main() {
val firstName: String? = "John"
val lastName: String? = "Doe"
// Combining multiple lets
firstName?.let { f ->
lastName?.let { l ->
println("$f $l")
}
}
}
```
## Resumen del Operador ?
| Operador | Descripción | Retorna |
|----------|-------------|---------|
| `?.` | Llamada segura | `null` si el receptor es `null` |
| `?:` | Operador Elvis | Valor por defecto si izquierda es `null` |
| `!!` | Aserción no-nula | Lanza NPE si es `null` |
| `as?` | Cast seguro | `null` si el cast falla |
## Seguridad Nula con Colecciones
```kotlin
fun main() {
val list: List? = listOf("a", "b", "c")
// Safe call with size
println(list?.size) // 3
list = null
// Empty list if null
val size = list?.size ?: 0
println(size) // 0
// Null list
val emptyList = list?.filter { it.isNotEmpty() } ?: emptyList()
println(emptyList) // []
}
```
## Tipos de Plataforma
Cuando se usa código Java desde Kotlin, los tipos de Java pueden ser tipos de plataforma (pueden ser null o no nulos):
```kotlin
// Java code
public class JavaExample {
public String getGreeting() {
return "Hello";
}
}
```
```kotlin
// Kotlin code calling Java
fun main() {
val javaExample = JavaExample()
val greeting: String = javaExample.getGreeting() // Non-null
// If Java method returns @Nullable String
// Then Kotlin sees it as String?
}
```
## Verificando Null en Condiciones
```kotlin
fun main() {
val str: String? = "Hello"
// Regular null check
if (str != null) {
println(str.length) // Smart cast to String
}
// After null check, smart cast applies
// Works only with local variables
}
```
### Nota sobre Casts Inteligentes
Los casts inteligentes no funcionan con variables mutables o propiedades que podrían ser cambiadas:
```kotlin
fun main() {
var str: String? = "Hello"
// Smart cast doesn't work because var can be changed
// str could be changed to null between check and use
if (str != null) {
// println(str.length) // Error: only safe call allowed
}
// Safe call works
println(str?.length)
}
```
## Resumen
- Kotlin distingue tipos anulables (`T?`) y no anulables (`T`)
- `?.` la llamada segura retorna null si el receptor es null
- `?:` el operador Elvis proporciona valor por defecto
- `!!` afirma no-null, lanza NPE si está mal
- `as?` cast seguro retorna null en caso de fallo
- `?.let { }` ejecuta bloque solo si no es null
- Prefiere llamadas seguras sobre `!!` en código de producción
- Los casts inteligentes funcionan con variables locales inmutables
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →