Skip to main content

Kotlin Late Init

Introduction

When working with Kotlin classes, you sometimes encounter situations where you can't initialize a property at the time of declaration, but you're sure it will be initialized before use. Kotlin provides a special modifier called lateinit for such scenarios, helping you avoid nullable types when you know a value will be available when needed.

In this tutorial, we'll explore what lateinit is, when to use it, and how it helps write cleaner Kotlin code.

What is lateinit?

The lateinit modifier allows you to declare non-null properties without initializing them at declaration time. It tells the Kotlin compiler: "I promise to initialize this property before accessing it."

kotlin
lateinit var message: String

The property above is declared but not initialized. It will be initialized later in the code before it's accessed.

When to use lateinit?

You should consider using lateinit in the following scenarios:

  1. When properties need to be initialized in a setup or initialization method (common in Android and frameworks)
  2. When properties are injected by dependency injection frameworks
  3. When properties are initialized in test setup methods

Rules and Limitations

Before diving into examples, let's understand the rules and limitations of lateinit:

  1. Can only be used with var properties (not with val)
  2. Can only be used with non-primitive types (String, custom classes, etc., but not Int, Boolean, etc.)
  3. Cannot be used with nullable types (since the whole point is to avoid nullability)
  4. Cannot be used with properties that have custom getters or setters

Basic Usage Examples

Example 1: Simple Late Initialization

kotlin
class Person {
lateinit var name: String

fun initialize(personName: String) {
name = personName
}

fun greet() {
println("Hello, $name!")
}
}

fun main() {
val person = Person()
// person.greet() // Would throw UninitializedPropertyAccessException

person.initialize("John")
person.greet() // Now it's safe
}

Output:

Hello, John!

If you had tried to access name before initializing it, Kotlin would throw an UninitializedPropertyAccessException.

Example 2: Checking if a lateinit Property is Initialized

Sometimes you need to check if a lateinit property has been initialized. Kotlin provides a special syntax for this:

kotlin
class Configuration {
lateinit var settings: Map<String, String>

fun isSettingsInitialized(): Boolean {
return ::settings.isInitialized
}

fun loadSettings(newSettings: Map<String, String>) {
settings = newSettings
}

fun displaySettings() {
if (::settings.isInitialized) {
println("Settings: $settings")
} else {
println("Settings not loaded yet")
}
}
}

fun main() {
val config = Configuration()

config.displaySettings()

val appSettings = mapOf(
"theme" to "dark",
"language" to "en",
"notifications" to "enabled"
)

config.loadSettings(appSettings)
config.displaySettings()
}

Output:

Settings not loaded yet
Settings: {theme=dark, language=en, notifications=enabled}

The syntax ::propertyName.isInitialized checks if a lateinit property has been initialized.

Real-World Applications

Example 1: Android Activity

In Android development, lateinit is commonly used for view binding:

kotlin
class MainActivity : AppCompatActivity() {
// We can't initialize this in the constructor, but it will be set in onCreate
lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize the binding
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// Now we can safely use binding
binding.welcomeText.text = "Hello, Android!"
}
}

Example 2: Dependency Injection

When using dependency injection frameworks like Dagger or Koin:

kotlin
class UserRepository {
lateinit var databaseService: DatabaseService
lateinit var networkService: NetworkService

// These services will be injected by the DI framework
fun inject(db: DatabaseService, network: NetworkService) {
this.databaseService = db
this.networkService = network
}

fun fetchUserData(userId: String): UserData {
val localData = databaseService.getUserById(userId)
return localData ?: networkService.fetchUser(userId)
}
}

Example 3: Testing

In unit testing, lateinit is useful for setting up test fixtures:

kotlin
class UserServiceTest {
lateinit var userService: UserService
lateinit var mockDatabase: DatabaseService

@Before
fun setup() {
mockDatabase = mock(DatabaseService::class.java)
userService = UserService(mockDatabase)
}

@Test
fun testGetUserName() {
// Test using the initialized properties
`when`(mockDatabase.getUserById("123")).thenReturn(User("123", "Alice"))
val userName = userService.getUserName("123")
assertEquals("Alice", userName)
}
}

Avoiding lateinit Initialization Exceptions

To avoid UninitializedPropertyAccessException, you can:

  1. Always ensure the property is initialized before access
  2. Check if the property is initialized using ::property.isInitialized
  3. Consider using a nullable type with proper null handling if initialization is truly optional
kotlin
class SafeHandler {
lateinit var resource: Resource

fun useResourceSafely() {
if (::resource.isInitialized) {
resource.process()
} else {
println("Resource not initialized yet")
}
}
}

When to Use lateinit vs. Nullable Types

Sometimes developers struggle deciding between using lateinit or nullable types. Here's a comparison:

Featurelateinit varNullable Type (var?)
Syntaxlateinit var prop: Typevar prop: Type? = null
Null checksNot neededRequired (using ?., !!, or ?:)
ExceptionThrows if accessed before initNo exception (returns null)
PrimitivesNot supportedSupported
Best forProperties guaranteed to be initializedTruly optional properties

Summary

The lateinit modifier in Kotlin provides a convenient way to declare non-null properties that will be initialized later in the code. It helps avoid excessive null checks while ensuring type safety. Remember that lateinit properties must be initialized before they're accessed to avoid runtime exceptions.

Key points to remember:

  • Use lateinit when a non-null property can't be initialized at declaration time
  • Only works with var properties and non-primitive types
  • Check initialization status with ::property.isInitialized
  • Consider alternatives like nullable types when appropriate

Exercises

  1. Create a class that uses lateinit for a property and initializes it in a method
  2. Write a function that safely checks if a lateinit property is initialized before using it
  3. Convert a class with nullable properties to use lateinit where appropriate
  4. Create a class that demonstrates a practical real-world usage of lateinit (like a cache or resource manager)

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)