Explain lateinit var versus val by lazy in Android

Tests deferred initialization and lifecycle coupling. Great answers contrast lateinit var's imperative assignment with lazy's first-access delegation, mapping lateinit to injected Activity fields and lazy to expensive computed objects.
WHAT THIS TESTS: Whether you understand the mechanical and semantic differences between two Kotlin deferred-initialization patterns, and more importantly whether you can match each pattern to the right Android lifecycle moment. Interviewers care about mutability guarantees, null safety, thread-safety defaults, and the performance implications of initialization timing.
A GOOD ANSWER COVERS four things in order. First, the mechanical split: lateinit var is a mutable non-nullable property that remains uninitialized until you assign it imperatively, and it can only be used with reference types, never primitives. Second, lazy is a delegate that runs a lambda on first access, caches the result, and returns an immutable val; under the hood it uses a synchronized lazy implementation by default, so it is thread-safe but carries a small locking cost. Third, the Android scenario for lateinit: injected dependencies like a ViewModel or Repository, or a view reference obtained via findViewById, where the object is created by a framework callback such as onCreate or onViewCreated and must be reassigned if the component is recreated. Fourth, the Android scenario for lazy: expensive initialization that might not happen on every code path, such as formatting a large dataset, building a RecyclerView adapter, or computing a complex bitmap, especially when the property may never be accessed during a particular user session.
COMMON WRONG ANSWERS: Saying lateinit works with val or with primitive types like Int; it requires var and a reference type. Claiming lazy is just syntax sugar for a nullable var with a custom getter, which misses the thread-safety and caching contract. Recommending lazy for view bindings inside an Activity without mentioning that the backing fragment or activity lifecycle might end before the lazy block runs, or conversely using lateinit for a one-time expensive computation that could safely be deferred. Another red flag is never mentioning the isInitialized check for lateinit, which is critical for defensive code in tests or conditional teardown.
LIKELY FOLLOW-UPS: How would you test a class that uses lateinit properties without hitting UninitializedPropertyAccessException? When would you switch lazy from SYNCHRONIZED to PUBLICATION or NONE, and what are the risks? Can you use lateinit with a custom delegate? How does by lazy interact with memory leaks if it captures an Activity context?
ONE CONCRETE EXAMPLE: In a Fragment, lateinit var is appropriate for a ViewModel obtained via ViewModelProvider inside onViewCreated, because the property must be mutable if the fragment is recreated and reassigned during a new lifecycle. By contrast, val dateFormatter by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) } is better inside the same fragment because the formatter is expensive to create, may not be used if the user never opens the date section, and should be immutable once built.
Source: kotlinlang.org
Read the original → kotlinlang.org
- #kotlin
- #android
- #property-delegation
- #lateinit
- #lazy
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.