Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
152: Late-Initialized Properties (lateinit)
I remember working with a developer a few years back who was migrating a large Java project to Kotlin. He had this DatabaseClient that needed to be initialized during a bootstrap phase—long after the class was instantiated, but before any business logic actually touched it. He did the "safe" thing and declared it as a nullable var DatabaseClient? = null. The problem is that he knew, with absolute certainty, that by the time any method was called, that client would be initialized. He spent half his day typing !! after every single reference to the client. It made the code noisy, fragile, and honestly, it defeated the whole point of Kotlin's null safety.
Breaking the Nullability Cycle
This is exactly where lateinit comes in. When you use lateinit var, you're essentially making a promise to the Kotlin compiler. You're saying, "I know you want this initialized in the constructor, but I can't do that right now. I promise to set its value before I ever try to read from it."
The magic here is that the property is treated as non-nullable. You don't have to deal with optional chaining (?.) or the dangerous non-null assertion operator (!!). You just use it like a regular, non-null variable. This is incredibly common in Android development (think onCreate) or when using dependency injection frameworks where the framework "injects" the dependency after the object is created.
class UserProfileManager {
// I can't initialize this here because I need the API key from a config file first
lateinit var sessionToken: String
fun initializeSession(token: String) {
sessionToken = token
}
fun printSession() {
// No null-checks needed!
println("Current session is: $sessionToken")
}
}
Now, there are a few strict rules you need to follow. First, lateinit only works with var, not val, because the value has to be changeable after the object is constructed. Second, it only works with non-nullable types. If you've already declared it as String?, lateinit is redundant and won't be allowed.
Verifying Initialization Status
Since you're promising the compiler that the variable will be ready, the compiler stops checking for you. If you break that promise—meaning you try to access the property before assigning a value to it—Kotlin will throw an UninitializedPropertyAccessException. It's a loud, crashing failure, which is actually preferable to a silent NullPointerException because it tells you exactly what went wrong.
Sometimes, however, you're in a situation where you aren't 100% sure if the initialization has happened yet. You don't want to crash the app just to check. In those cases, you can use a reflection-based check using the :: operator. I don't use this often, but it's a lifesaver in complex lifecycle events.
if (this::sessionToken.isInitialized) {
println("We are good to go!")
} else {
println("Still waiting for the token...")
}
Just a quick tip: don't over-use this. If you find yourself using lateinit everywhere, it might be a sign that your class is taking on too many responsibilities or that your dependency graph is a bit too tangled. Use it for the specific cases where the lifecycle of the object and the availability of the data simply don't align.
📋 Practical Task
Refactoring the AppConfigurationStore
You've inherited a piece of code for an AppConfigurationStore. The previous developer used nullable types and the !! operator to handle a configuration string that is loaded from a remote server. This is making the code hard to read and prone to runtime crashes.
Your Task:
- Refactor the
appConfigUrlproperty to uselateinit. - Remove the nullable type declaration (
String?). - Remove the non-null assertions (
!!) from thefetchSettings()method. - Ensure the
loadUrl()method correctly assigns the value to thelateinitproperty.
class AppConfigurationStore {
// Change this to lateinit
var appConfigUrl: String? = null
fun loadUrl(url: String) {
appConfigUrl = url
}
fun fetchSettings() {
// Remove the !! and treat it as a non-null String
println("Fetching settings from ${appConfigUrl!!}")
}
}
fun main() {
val store = AppConfigurationStore()
store.loadUrl("https://api.example.com/config")
store.fetchSettings()
}There are no comments for now.