Question
In Kotlin, how can you check whether a lateinit property has already been initialized before using it?
For example:
class Foo {
private lateinit var myFile: File
fun bar(path: String?) {
path?.let { myFile = File(it) }
}
fun bar2() {
myFile.whateverMethod()
// This may crash if myFile has not been initialized yet
}
}
I want to know whether there is a safe way to detect if myFile has been initialized before accessing it.
Short Answer
By the end of this page, you will understand what Kotlin lateinit properties are, how to check whether they have been initialized, when that check is useful, and what alternatives like nullable properties or lazy delegation may be better in some situations.
Concept
lateinit in Kotlin lets you declare a non-null var without assigning a value immediately.
lateinit var name: String
This is useful when a value cannot be created at object construction time, but you promise it will be assigned before use.
However, if you access a lateinit property before assigning it, Kotlin throws an UninitializedPropertyAccessException.
That is exactly the risk in this example:
private lateinit var myFile: File
If bar2() runs before bar() assigns myFile, the program can crash.
Kotlin provides a built-in way to check whether a lateinit property is initialized:
this::myFile.isInitialized
This uses a property reference. It asks Kotlin for metadata about the property and returns true if the property has already received a value.
Important points:
lateinitworks only with non-null mutable properties (var)- It cannot be used with primitive types like
Int,Boolean, orDouble isInitializedis available only forlateinitproperties- The check is typically used inside the same class, or where the property reference is accessible
Why this matters in real programming:
- It helps avoid runtime crashes
- It is useful for lifecycle-based code, such as Android components, tests, or delayed setup
- It makes initialization state explicit when a value is not ready at construction time
That said, checking isInitialized is not always the best design. Sometimes a nullable property or a different initialization strategy is clearer and safer.
Mental Model
Think of a lateinit property like a locker with a label on it, but nothing has been placed inside yet.
- Declaring the locker means the space exists
- Initializing it means putting the item inside
- Accessing it before putting anything inside causes an error
isInitializedis like checking whether the locker already contains something
So instead of blindly opening the locker and expecting an item, you first check whether it has been filled.
Syntax and Examples
The basic syntax for checking a lateinit property is:
if (this::myProperty.isInitialized) {
// safe to use myProperty
}
Example with your case
import java.io.File
class Foo {
private lateinit var myFile: File
fun bar(path: String?) {
path?.let { myFile = File(it) }
}
fun bar2() {
if (this::myFile.isInitialized) {
println(myFile.path)
} else {
println("myFile is not initialized yet")
}
}
}
What this does
this::myFilecreates a reference to the property.isInitializedchecks whether thelateinitvariable has been assigned- If it has, you can use
myFile - If not, you handle that case safely
Another useful pattern: guard clause
fun bar2() {
if (!this::myFile.isInitialized) {
println("File is not ready")
return
}
println(myFile.path)
}
This pattern keeps the main logic clean by exiting early when the property is unavailable.
Alternative: use nullable instead
Sometimes lateinit is not the best choice.
import java.io.File
class Foo {
private var myFile: File? = null
fun bar(path: String?) {
path?.let { myFile = File(it) }
}
fun bar2() {
myFile?.let {
println(it.path)
} ?: println("myFile is null")
}
}
This makes the possibility of missing data part of the type system.
Step by Step Execution
Consider this example:
import java.io.File
class Foo {
private lateinit var myFile: File
fun setFile(path: String?) {
path?.let { myFile = File(it) }
}
fun printFilePath() {
if (this::myFile.isInitialized) {
println(myFile.path)
} else {
println("No file has been set")
}
}
}
Now trace it step by step.
Case 1: property not initialized
val foo = Foo()
foo.printFilePath()
Execution:
Foo()creates an objectmyFileexists as alateinitproperty, but no value has been assigned yetprintFilePath()runsthis::myFile.isInitializedreturnsfalse- The
elsebranch runs - Output:
No file has been set
Case 2: property initialized first
val foo = Foo()
foo.setFile("/tmp/data.txt")
foo.printFilePath()
Execution:
Foo()creates an objectsetFile("/tmp/data.txt")runspath?.let { ... }executes becausepathis not nullmyFile = File(it)assigns a realFileobject tomyFileprintFilePath()runsthis::myFile.isInitializedreturnstrueprintln(myFile.path)executes safely
What happens without the check
fun printFilePath() {
println(myFile.path)
}
If myFile was never assigned, Kotlin throws:
kotlin.UninitializedPropertyAccessException
That is why the check exists.
Real World Use Cases
lateinit initialization checks appear in situations where setup happens later than object creation.
Android and UI lifecycle
A view or binding object may be assigned in a lifecycle method and accessed later.
if (this::binding.isInitialized) {
binding.textView.text = "Ready"
}
Dependency injection
A dependency may be injected after construction.
lateinit var repository: UserRepository
Before using it in some special flow, you might verify it has been assigned.
Test setup
In unit tests, objects are often initialized in @Before methods.
lateinit var service: UserService
A check can help in utility methods if setup is conditional.
Delayed resource creation
A file, socket, parser, or cache object might only be created if configuration is valid.
path?.let { myFile = File(it) }
Then later code can safely check whether setup succeeded.
Optional component wiring
In plugin-style systems, some parts of the app may only be initialized when enabled in configuration.
Real Codebase Usage
In real projects, developers usually use lateinit carefully and combine it with clear control flow.
Common pattern: guard clause
fun processFile() {
if (!this::myFile.isInitialized) return
println(myFile.path)
}
This is simple and avoids deeply nested if statements.
Common pattern: fail fast with a clear message
fun processFile() {
check(this::myFile.isInitialized) { "myFile must be initialized before calling processFile()" }
println(myFile.path)
}
This is useful when missing initialization is a programmer error, not a normal runtime case.
Common pattern: initialize in one place
Instead of checking everywhere, many codebases centralize setup.
fun initialize(path: String) {
myFile = File(path)
}
Then other methods assume initialization has already happened.
Common pattern: prefer nullable when absence is valid
If not having a value is normal, teams often prefer:
private var myFile: File? = null
This makes the optional state visible in the type.
Common pattern: use lazy for computed values
If the value should be created automatically on first use, lazy is often cleaner.
val config by lazy { loadConfig() }
This removes the need for manual initialization checks.
Common pattern: validation before assignment
fun setPath(path: String?) {
if (path.isNullOrBlank()) return
myFile = File(path)
}
Developers often combine validation with delayed initialization to ensure the property only becomes available when input is valid.
Common Mistakes
1. Accessing a lateinit property without checking
Broken code:
fun bar2() {
println(myFile.path)
}
Problem:
- This crashes if
myFilewas never assigned
Fix:
fun bar2() {
if (this::myFile.isInitialized) {
println(myFile.path)
}
}
2. Using lateinit when null is a valid state
Broken design:
private lateinit var selectedFile: File
Problem:
- If "no file selected yet" is a normal state,
lateinithides that fact
Better:
private var selectedFile: File? = null
3. Thinking lateinit makes initialization automatic
Broken assumption:
private lateinit var config: Config
fun useConfig() {
println(config.name)
}
Problem:
lateinitonly delays initialization; it does not perform it
4. Trying to use lateinit with primitive types
Broken code:
lateinit var count: Int
Problem:
- Kotlin does not allow this
Use one of these instead:
var count: Int = 0
or
var count: Int? = null
5. Overusing isInitialized
Problem:
- If every method checks initialization, the design may be unclear
- It can signal that the object can be used in too many invalid states
Better approaches may include:
- constructor injection
- nullable properties
lazy- explicit initialization functions
6. Checking initialization from the wrong place
isInitialized is meant for accessible lateinit property references. In practice, it is most commonly used from within the declaring class.
Safe example:
class Foo {
lateinit var value: String
fun ready(): Boolean {
return this::value.isInitialized
}
}
Comparisons
| Concept | What it means | Best when | Main drawback |
|---|---|---|---|
lateinit var x: Type | Non-null property assigned later | You guarantee assignment before use | Can crash if accessed too early |
var x: Type? = null | Nullable property | Missing value is a normal state | Requires null handling |
val x by lazy { ... } | Value created on first access | Value can be computed automatically once | Read-only and not manually reassigned |
| Constructor parameter | Value required at creation time | Object should never exist without it | Less flexible for delayed setup |
lateinit vs nullable
private lateinit var myFile: File
private var myFile2: File? = null
Use lateinit when:
- the property should conceptually never be null
- initialization happens later due to lifecycle or framework rules
Use nullable when:
- absence is normal
- your code should explicitly handle missing values
lateinit vs lazy
val fileName by lazy { readNameFromDisk() }
Use lazy when:
- the value can be computed automatically
- you want initialization on first access
- the property does not need reassignment
Use lateinit when:
- the value will be provided externally
- the property must be mutable
lateinit vs constructor injection
class Foo(private val myFile: File)
Constructor injection is usually the cleanest option when the value is required for the object to work.
Use it when:
- the object should always start in a valid state
- you want fewer runtime checks
Cheat Sheet
lateinit var name: String
Check initialization:
if (this::name.isInitialized) {
println(name)
}
Guard clause pattern:
if (!this::name.isInitialized) return
Fail-fast pattern:
check(this::name.isInitialized) { "name must be initialized" }
Rules:
lateinitworks only withvar- property must be non-null
- not allowed for primitive types like
IntorBoolean - accessing before initialization throws
UninitializedPropertyAccessException isInitializedworks only forlateinitproperties
Prefer nullable instead when missing data is normal:
var file: File? = null
Prefer lazy when the value can be created automatically:
val config by lazy { loadConfig() }
FAQ
How do I check if a lateinit variable is initialized in Kotlin?
Use a property reference with isInitialized:
if (this::myFile.isInitialized) {
// use myFile
}
What happens if I access a lateinit property before initialization?
Kotlin throws an UninitializedPropertyAccessException at runtime.
Can I use lateinit with val?
No. lateinit only works with mutable properties declared using var.
Can I use lateinit with Int or other primitive types?
No. lateinit is not allowed for primitive types.
Should I use lateinit or a nullable property?
Use lateinit when the value should never logically be null but is assigned later. Use a nullable property when absence is a normal state.
Is isInitialized available for any property?
No. It is specifically for lateinit properties.
Is lateinit better than constructor injection?
Usually not. If a value is required for the object to work, constructor injection is often cleaner and safer.
When is lateinit commonly used?
It is often used in Android lifecycle code, dependency injection, test setup, and delayed resource initialization.
Mini Project
Description
Build a small Kotlin class that manages a log file path. The file should only be created when a valid path is provided, and another method should safely print file information only if the lateinit property has been initialized. This demonstrates delayed initialization and safe access in a realistic way.
Goal
Create a class that safely uses a lateinit File property without crashing when the file has not been set yet.
Requirements
- Create a class with a
lateinitproperty of typeFile - Add a method that initializes the file only when the path is not null or blank
- Add a method that checks whether the property is initialized before using it
- Print a clear message when the file is not available yet
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.