ProgramaciónDesarrollador de Kotlin

¿Cómo se implementa la palabra clave 'inline' en relación con las clases (value class/inline class) en Kotlin? ¿Qué restricciones existen, cómo funcionan estas clases a nivel de bytecode, cuándo y por qué deberían usarse? Proporcione un ejemplo y explique las complejidades típicas.

Supere entrevistas con el asistente de IA Hintsage

Respuesta

En Kotlin, inline class (a partir de Kotlin 1.5 — el término "value class"), permite crear envoltorios sobre tipos con un costo mínimo. Durante la compilación, estas clases son reemplazadas en secreto por su valor interno (value) para evitar sobrecostes por la creación de objetos.

Restricciones y características:

  • Solo puede haber una propiedad en el constructor primario.
  • No se permite almacenar referencias para la igualdad referencial (=== no funciona como normalmente).
  • La value class no puede ser una clase heredable y no puede tener estado aparte de su value.
  • No todos los tipos genéricos y APIs de plataforma pueden trabajar con inline/value class sin boxing.
  • La value class no puede tener un bloque init, campos además de value y solo funciones mínimas.

Ejemplo:

@JvmInline value class UserId(val value: String) fun getUser(id: UserId) { println("Cargando usuario con id: ${id.value}") } val id = UserId("XYZ") getUser(id) // Bajo el capó, simplemente trabaja con String!

Cuándo usar:

  • Para garantizar la seguridad de tipos para identificadores, valores especiales.
  • Mejorar el rendimiento cuando se trabaja con millones de tales envoltorios (no se crean objetos).

Pregunta trampa

¿Se puede heredar una value class o usarla en una jerarquía de interfaces/clases abstractas?

Respuesta: No, la value class no puede heredar otras clases (excepto interfaces), no puede ser abierta a la herencia, no permite el bloque init y otros campos no estáticos. La única opción disponible es implementar interfaces.

Ejemplo:

interface Validatable { fun isValid(): Boolean } @JvmInline value class Email(val raw: String) : Validatable { override fun isValid() = raw.contains("@") }

Ejemplos de errores reales debido al desconocimiento de las sutilezas del tema


Historia

Una aplicación de Android experimentó un aumento repentino en el tiempo de inicio después de agregar value class a los parámetros Parcelable: se descubrió que un @Parcelize incorrecto con value class provocaba boxing/unboxing en cada etapa de la serialización, rompiendo las ventajas de inline.


Historia

Un microservicio comenzó a utilizar activamente value class para UserId y ProductId por motivos de seguridad de tipos, pero en muchos lugares las funciones genéricas requerían reflexión, que no funcionaba con la "envoltura". Las pruebas unitarias comenzaron a fallar inesperadamente, aparecieron ClassCastException.


Historia

El código migrado de Java comenzó a reemplazar las clases de dominio internas por value class para optimización, pero su uso como campos nulos llevó a excepciones de puntero nulo inesperadas, ya que la value class solo puede ser null si el valor externo también es null, lo que rompió invariantes antiguos.