1.1.50.17. fejezet, Beágyazott értékosztályok
value class Password(private val s: String)
Néha szükségünk van arra, hogy becsomagoljunk alaptípusokat osztályokba. Performencia szempontjából ez azonban nem szerencsés megoldás, mivel a JVM optimalizálja az alaptípusokkal kapcsolatos műveleteket. Ezen segít az inline oszály. JVM környezethez használjuk a @JvmInline annotációt.
@JvmInline value class Password(private val s: String) // No actual instantiation of class 'Password' happens // At runtime 'securePassword' contains just 'String' val securePassword = Password("Don't try this in production")
Tagok
@JvmInline value class Person(private val fullName: String) { init { require(fullName.isNotEmpty()) { "Full name shouldn't be empty" } } constructor(firstName: String, lastName: String) : this("$firstName $lastName") { require(lastName.isNotBlank()) { "Last name shouldn't be empty" } } val length: Int get() = fullName.length fun greet() { println("Hello, $fullName") } } fun main() { val name1 = Person("Kotlin", "Mascot") val name2 = Person("Kodee") name1.greet() // the `greet()` function is called as a static method println(name2.length) // property getter is called as a static method }
Öröklődés
interface Printable { fun prettyPrint(): String } @JvmInline value 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 }
Az inline osztályok számára tilos az osztályhierarchiában való részvétel. Ez azt jelenti, hogy az inline osztályok nem bővíthetnek más osztályokat, és mindig final
Reprezentáció
A generált kódban a Kotlin fordító minden inline osztályhoz egy wrapper-t tart. A beágyazott osztálypéldányok futásidőben wrapper-ként vagy mögöttes típusként is ábrázolhatók. Ez hasonló ahhoz, ahogy az Int ábrázolható primitívként és Integer wrapper-ként.
interface I @JvmInline value class Foo(val i: Int) : I fun asInline(f: Foo) {} fun <T> asGeneric(x: T) {} fun asInterface(i: I) {} fun asNullable(i: Foo?) {} fun <T> 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) }
@JvmInline value class UserId<T>(val value: T) fun compute(s: UserId<String>) {} // compiler generates fun compute-<hashcode>(s: Any?)
Mangling
Mivel az inline osztályok az alapul szolgáló típusuknak megfelelően vannak lefordítva, ez különböző homályos hibákhoz vezethet, például váratlan platformaláírás-ütközésekhez.
Az ilyen problémák enyhítése érdekében a beágyazott osztályokat használó függvények úgy vannak összezavarva, hogy stabil hashkódot adnak hozzá a függvény nevéhez. (pl.: public final void compute-
@JvmInline value 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) { }
Meghívás Java kódból
@JvmInline value class UInt(val x: Int) fun compute(x: Int) { } @JvmName("computeUInt") fun compute(x: UInt) { }
Beágyazott osztályok és típus aliaszok
typealias NameTypeAlias = String @JvmInline value 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 }
Beágyazott osztályok és delegáció
interface MyInterface { fun bar() fun foo() = "foo" } @JvmInline value class MyInterfaceWrapper(val myInterface: MyInterface) : MyInterface by myInterface fun main() { val my = MyInterfaceWrapper(object : MyInterface { override fun bar() { // body } }) println(my.foo()) // prints "foo" }
- A hozzászóláshoz be kell jelentkezni