Las clases en línea son introducidas por Kotlin desde la versión Kotlin 1.3 para superar las deficiencias de los contenedores tradicionales en torno a algunos tipos. Estas clases en línea agregan la bondad de Typealiases con el rango de valores de los tipos de datos primitivos.
Supongamos que estamos vendiendo algunos artículos y el costo se define como un tipo flotante. Esto se representa en la siguiente clase de datos
data class Items(val itemno: Int, val cost: float, val qty: Int)
Si admitimos dos tipos de monedas como el dólar y las rupias, necesitamos refactorizar el costo en otra clase.
Java
data class Items(val itemno: Int, val cost: Cost, val qty: Int) data class Cost(val value: Float, val currency: Currency) enum class Currency { RUPEE, DOLLAR }
El método anterior tiene dos problemas:
1. Sobrecarga de memoria
2. Complejidad
Estos dos problemas se superan con las clases en línea
Java
data class Item(val id: Int, val price: RupeePrice, val qty: Int) inline class RupeePrice(val price: Float) { inline fun toDollars(): Float = price * 71.62f }
Una clase en línea debe tener una sola propiedad inicializada en el constructor principal. En tiempo de ejecución, las instancias de la clase en línea se representarán utilizando esta propiedad única: los datos de la clase están «en línea» en sus usos (por eso el nombre «clases en línea»).
miembros
Son similares a las clases regulares en el sentido de que se les permite declarar propiedades y funciones. Sin embargo, también tienen ciertas limitaciones. Las clases en línea no pueden tener bloques init ni pueden tener propiedades computables complejas como lateinit/ propiedades delegadas.
Java
inline class Name(val s: String) { val length: Int get() = s.length fun greet() { println("Hello, $s") } } fun main() { val name = Name("Kotlin") name.greet() // method `greet` is called as a static method println(name.length) // property getter is called as a static method }
Herencia
Estas clases pueden heredar de Interfaces pero no pueden extender otras clases y deben ser finales
Java
interface Printable { fun prettyPrint(): String } inline class Name(val s: String) : Printable { override fun prettyPrint(): String = "Let's $s!" } fun main() { val name = Name("Kotlin") println(name.prettyPrint()) // Still called as a static method }
Representación
Las clases en línea se pueden representar como envoltorios o como tipo subyacente. Aunque se prefiere este último, a veces es útil mantener los envoltorios alrededor. Necesariamente, se encuadran cuando se usan como otro tipo. La igualdad referencial no tiene sentido, ya que puede representarse como un valor subyacente y como un envoltorio.
Java
interface I inline class Foo(val i: Int) : I fun asInline(f: Foo) {} fun asGeneric(x: T) {} fun asInterface(i: I) {} fun asNullable(i: Foo?) {} fun id(x: T): T = x fun main() { val f = Foo(42) asInline(f) // unboxed: used as Foo itself asGeneric(f) // boxed: used as generic type T asInterface(f) // boxed: used as type I asNullable(f) // boxed: used as Foo?, which is different from Foo // below, 'f' first is boxed (while being passed to 'id') and then unboxed (when returned from 'id') // In the end, 'c' contains unboxed representation (just '42'), as 'f' val c = id(f) }
Como un tipo subyacente, estas clases en línea pueden generar errores oscuros, como fallas en la firma de la plataforma.
Java
inline class UInt(val x: Int) // Represented as 'public final void compute(int x)' on the JVM fun compute(x: Int) { } // Also represented as 'public final void compute(int x)' on the JVM! fun compute(x: UInt) { }
Para evitar tales errores, usamos un proceso llamado Mangling en el que agregamos código hash al nombre de la función. Por lo tanto, el cálculo divertido (x: UInt) se representará como cálculo vacío final público-(int x) , lo que resuelve el problema.
Clases en línea frente a alias de tipo
Aunque ambos pueden parecer similares, los alias de tipo son compatibles con la asignación del tipo subyacente. Además, las clases en línea introducen un tipo completamente nuevo, mientras que los alias de tipo dan un nombre alternativo para el tipo existente.
Java
typealias NameTypeAlias = String inline class NameInlineClass(val s: String) fun acceptString(s: String) {} fun acceptNameTypeAlias(n: NameTypeAlias) {} fun acceptNameInlineClass(p: NameInlineClass) {} fun main() { val nameAlias: NameTypeAlias = "" val nameInlineClass: NameInlineClass = NameInlineClass("") val string: String = "" acceptString(nameAlias) // OK: pass alias instead of underlying type acceptString(nameInlineClass) // Not OK: can't pass inline class instead of underlying type // And vice versa: acceptNameTypeAlias(string) // OK: pass underlying type instead of alias acceptNameInlineClass(string) // Not OK: can't pass underlying type instead of inline class }
El diseño de las clases en línea es nuevo y no se ofrecen garantías de compatibilidad. En Kotlin 1.3+, se informará una advertencia que indica que esta función es experimental. Para eliminar esto, debemos aceptar el uso de esta función experimental pasando el argumento del compilador -Xinline-classes.
Publicación traducida automáticamente
Artículo escrito por nithya shree y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA