You are currently viewing Kotlin Inline Value Class

Kotlin Inline Value Class

What are Inline Classes?

An inline class is a special type of class that is designed to wrap a single value and is optimized by the compiler to avoid creating an additional object at runtime. Inline classes are a subset of value-based classes. They don’t have an identity and can only hold values. Instances of the inline class will be represented by this single property at runtime.

How to declare Inline Class ?

Inline classes can be declared using the value modifier before the name of the class and also by adding the @JvmInline annotation before the class declaration. This annotation indicates that the class should be treated as an inline class, optimizing it at the bytecode level. Let’s take a example

@JvmInline
value class Username(val value: String)
Note : Inline classes have some restrictions
  • They can only have one property in their primary constructor. Primary constructor of Inline class must only have final read-only (val) property parameters
  • The wrapped type must be a primitive type (e.g., Int, Long, Char, etc.) or another inline class.

Example of an Inline class

Let’s consider an example where inline classes are used to represent user authentication credentials, specifically a username and a hashed password.

// Define inline classes for username and hashed password
@JvmInline
value class Username(val value: String)
@JvmInline
value class HashedPassword(val value: String)

We will use the Username and HashedPassword inline classes to represent a user’s authentication credentials.

// Function to authenticate a user
fun authenticateUser(username: Username, password: HashedPassword): Boolean {
    // In a real-world scenario, you would perform authentication logic here
    // For simplicity, let's just check if the lengths of username and password match
    return username.value.length > 0 && password.value.length > 0
}

fun main() {
    // Simulating Android usage in an activity or fragment

    // Create username and hashed password objects using inline classes
    val user1 = Username("john_doe")
    val password1 = HashedPassword("hashed_password_123")

    val user2 = Username("jane_smith")
    val password2 = HashedPassword("hashed_password_456")

    // Authenticate users using the inline classes
    val isAuthenticated1 = authenticateUser(user1, password1)
    val isAuthenticated2 = authenticateUser(user2, password2)

    // Display authentication results
    println("User 1 authenticated: $isAuthenticated1")
    println("User 2 authenticated: $isAuthenticated2")
}

The authenticateUser function takes Username and HashedPassword objects and performs a simple authentication check based on the lengths of the provided credentials.

Inline Class members

Some of the functionality of ordinary classes is supported by inline classes. Specifically, they can specify functions and properties. Inline class can have an init block, and secondary constructors. Let’s take an example:

@JvmInline
value class UserProfile(val username: String) {
    init {
        require(username.isNotEmpty()) {
            "Username 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() = username.length

    // Function to display user profile information
    fun displayProfileInfo() {
        println("Username: $username")
    }
}

In above inline class:

  • @JvmInline: This annotation indicates that the class should be treated as an inline class, optimizing it at the bytecode level.
  • value class UserProfile(val username: String): Defines an inline class named UserProfile with a primary constructor that takes a single property username.
  • The UserProfile inline class has a primary constructor with only one property (username).
  • An init block checks that the username is not empty.
  • The displayProfileInfo function is used to print the user profile information.
  • require(username.isNotEmpty()) { "Username shouldn't be empty" }: Checks that the username property is not empty, throwing an IllegalArgumentException if the condition is not met.
  • val length: Int: Adds a property length that returns the length of the username property.
  • fun displayProfileInfo(): Defines a function displayProfileInfo to print the username.
  • Secondary constructor that takes firstName and lastName, concatenates them, and delegates to the primary constructor.
fun main() {
    // Create UserProfile objects
    val userProfile1 = UserProfile("JohnDoe")
    val userProfile2 = UserProfile("JaneSmith")

    // Access the wrapped values and display profile information
    println("User 1 - Username: ${userProfile1.username}")
    userProfile1.displayProfileInfo()
    println("username length ${userProfile1.length}") 

    println("User 2 - Username: ${userProfile2.username}")
    userProfile2.displayProfileInfo()
    println("username length ${userProfile2.length}") 

}

In this example Main Function:

  • Creates two instances of UserProfile (userProfile1 and userProfile2) using different constructors.
  • Prints the username and calls displayProfileInfo for each instance.
  • Prints the length of the username using the length property.

When you run this program you will see following output:

User 1 - Username: JohnDoe
Username: JohnDoe
username length 7
User 2 - Username: JaneSmith
Username: JaneSmith
username length 9

Inheritance

Inline class in Kotlin support inheritance, Inline classes allows to inherit only from interfaces. Inline classes cannot extend other classes and are always final. Let’s take an example:

interface Audible {
    fun makeSound(): String
}

@JvmInline
value class AnimalSound(val sound: String) : Audible {
    override fun makeSound(): String = "The animal says: $sound"
}

fun main() {
    val catSound = AnimalSound("Meow")
    val dogSound = AnimalSound("Woof")

    println(catSound.makeSound()) // Output: The animal says: Meow
    println(dogSound.makeSound()) // Output: The animal says: Woof
}

Here is the output of this program

The animal says: Meow
The animal says: Woof

Use case of Inline classes

1. Immutable Value Objects:

Inline classes are immutable, which makes them suitable for creating value objects. The immutability ensures that the wrapped value cannot be changed after instantiation.

2. Domain-Specific Types:

Inline classes are useful for creating domain-specific types, such as representing specific measurements, identifiers, or units.

@JvmInline
value class Distance(val meters: Double)

fun calculateTotalDistance(distances: List<Distance>): Distance {
    // Code for calculating the total distance
}

3. Optimization:

Inline classes are optimized at the bytecode level, reducing the runtime overhead associated with creating objects. This can be beneficial in performance-sensitive scenarios.

For more information about Inline class, you can explore the details provided in the official Kotlin documentation on Inline value classes.

Also Read :