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 namedUserProfile
with a primary constructor that takes a single propertyusername
.- 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 theusername
property is not empty, throwing anIllegalArgumentException
if the condition is not met.val length: Int
: Adds a propertylength
that returns the length of theusername
property.fun displayProfileInfo()
: Defines a functiondisplayProfileInfo
to print the username.- Secondary constructor that takes
firstName
andlastName
, 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
anduserProfile2
) 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.