Kotlin Inline classes

Inline classes are introduced by Kotlin since Kotlin 1.3 version to overcome the shortcomings of traditional wrappers around some types.These Inline classes add the goodness of Typealiases with the value range of the primitive data types.

Let us suppose that we are selling some items and the cost is defined as a float type.This is depicted in the following data class

data class Items(val itemno: Int, val cost: float, val qty: Int)

If we support two types of currencies like dollar and rupees, we need to refactor cost in another class.



filter_none

edit
close

play_arrow

link
brightness_4
code

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
}

chevron_right


The above method has two probems:
1.Memory overhead
2.Complexity
These two problems are overcome by Inline classes

filter_none

edit
close

play_arrow

link
brightness_4
code

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
}

chevron_right


An inline class must have a single property initialized in the primary constructor. At runtime, instances of the inline class will be represented using this single property:data of the class is “inlined” into its usages (That’s why the name “Inline classes”).

Members

They are similar to regular classes in the sense that they are allowed to declare properties and functions.However they have certain limitations too.Inline classes cannot have init blocks nor can they have complex computable properties like lateinit/delegated properties.

filter_none

edit
close

play_arrow

link
brightness_4
code

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
}

chevron_right


Inheritance

These classes are allowed to inherit from Interfaces but can not extend other classes and must be final

filter_none

edit
close

play_arrow

link
brightness_4
code

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
}

chevron_right


Representation

Inline classes can be represented as either wrappers or underlying type.Though the latter is preferred, sometimes it is useful to keep wrappers around.Necessarily they are boxed whenever used as other type. Referential equality is meaningless as it can be represented both as an underlying value and as a wrapper.

filter_none

edit
close

play_arrow

link
brightness_4
code

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)  
}

chevron_right


As an underlying type, these inline classes may lead to obscure errors like platform signature crashes.

filter_none

edit
close

play_arrow

link
brightness_4
code

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) { }

chevron_right


To prevent such errors we use a process called Mangling where we add some hashcode to function name.Therefore, fun compute(x: UInt) will be represented as public final void compute-(int x), which solves the problem.

Inline classes vs type aliases

Though both may appear similar, the type aliases are assignment-compatible with underlying type.Also inline classes introduce a completely new type whereas type aliases give an alternate name for existing type

filter_none

edit
close

play_arrow

link
brightness_4
code

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
}

chevron_right


The design of inline classes is new and no compatibility guarantees are given.In Kotlin 1.3+, a warning will be reported, indicating that this feature is experimental.To remove this we have to opt in to the usage of this experimental feature by passing the compiler argument -Xinline-classes.



My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :

Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.