Kotlin
Inline classes
Swift
|
inline class Password(val value: String)
|
inline class Password(let value: String)
|
value class Password(private val s: String)
|
value class Password(private let s: String)
|
// For JVM backends
@JvmInline
value class Password(private val s: String)
|
// For JVM backends
@JvmInline
value class Password(private let s: String)
|
// No actual instantiation of class 'Password' happens
// At runtime 'securePassword' contains just 'String'
val securePassword = Password("Don't try this in production")
|
// No actual instantiation of class 'Password' happens
// At runtime 'securePassword' contains just 'String'
let securePassword = Password("Don't try this in production")
|
Members
|
@JvmInline
value class Name(val s: String) {
init {
require(s.length > 0) { }
}
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
}
|
@JvmInline
value class Name(let s: String) {
init {
require(s.length > 0) { }
}
let length: Int
get() = s.length
func greet() {
print("Hello, $s")
}
}
func main() {
let name = Name("Kotlin")
name.greet() // method `greet` is called as a static method
print(name.length) // property getter is called as a static method
}
|
Inheritance
|
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
}
|
interface Printable {
func prettyPrint() -> String
}
@JvmInline
value class Name(let s: String) : Printable {
override func prettyPrint() -> String = "Let's $s!"
}
func main() {
let name = Name("Kotlin")
print(name.prettyPrint()) // Still called as a static method
}
|
Representation
|
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)
}
|
interface I
@JvmInline
value class Foo(let i: Int) : I
func asInline(f: Foo) {}
func <T> asGeneric(x: T) {}
func asInterface(i: I) {}
func asNullable(i: Foo?) {}
func <T> id(x: T): T = x
func main() {
let 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'
let c = id(f)
}
|
Mangling
|
@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) { }
|
@JvmInline
value class UInt(let x: Int)
// Represented as 'public final void compute(int x)' on the JVM
func compute(x: Int) { }
// Also represented as 'public final void compute(int x)' on the JVM!
func compute(x: UInt) { }
|
Calling from Java code
|
@JvmInline
value class UInt(val x: Int)
fun compute(x: Int) { }
@JvmName("computeUInt")
fun compute(x: UInt) { }
|
@JvmInline
value class UInt(let x: Int)
func compute(x: Int) { }
@JvmName("computeUInt")
func compute(x: UInt) { }
|
Inline classes vs type aliases
|
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
}
|
typealias NameTypeAlias = String
@JvmInline
value class NameInlineClass(let s: String)
func acceptString(s: String) {}
func acceptNameTypeAlias(n: NameTypeAlias) {}
func acceptNameInlineClass(p: NameInlineClass) {}
func main() {
let nameAlias: NameTypeAlias = ""
let nameInlineClass: NameInlineClass = NameInlineClass("")
let 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
}
|
Gradle
|
kotlin {
sourceSets.all {
languageSettings.enableLanguageFeature('InlineClasses')
}
}
|
kotlin {
sourceSets.all {
languageSettings.enableLanguageFeature('InlineClasses')
}
}
|
kotlin {
sourceSets.all {
languageSettings.enableLanguageFeature("InlineClasses")
}
}
|
kotlin {
sourceSets.all {
languageSettings.enableLanguageFeature("InlineClasses")
}
}
|
Maven
|
<configuration>
<args>
<arg>-Xinline-classes</arg>
</args>
</configuration>
|
<configuration>
<args>
<arg>-Xinline-classes</arg>
</args>
</configuration>
|