Question
How can I declare a SharedPreferences field in a Kotlin Android base activity and assign it later in onCreate()?
In Java, I can declare the field first and initialize it later:
protected SharedPreferences mSharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
}
I want the Kotlin equivalent so that subclasses of the base activity can use the property:
protected var sharedPreferences: SharedPreferences
Then, in onCreate():
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
However, Kotlin reports: Property must be initialized or be abstract. Why does this happen, and how should the property be declared?
Short Answer
Kotlin requires non-null properties to have a value when an object is created. For Android properties that are assigned later in lifecycle methods such as onCreate(), use lateinit var when the property is guaranteed to be initialized before use. Nullable properties and lazy initialization are alternatives with different trade-offs.
Concept
Java lets an uninitialized reference field contain null by default. Kotlin is designed to make possible null values explicit: a property declared as SharedPreferences is non-null, so Kotlin requires it to receive a value during construction.
This declaration is therefore incomplete:
protected var sharedPreferences: SharedPreferences
Kotlin cannot prove that onCreate() will run before another method accesses sharedPreferences. It requires one of these choices:
- Give the property an initial value.
- Make it nullable with
SharedPreferences?. - Use
lateinit varto promise that it will be initialized before access. - Use
by lazywhen its value can be calculated on first access.
For a SharedPreferences field initialized in an Activity's onCreate(), the usual Kotlin declaration is:
protected lateinit var sharedPreferences: SharedPreferences
lateinit is appropriate only for a mutable (var), non-null reference property. It postpones initialization while keeping the property's type non-null.
Mental Model
Think of a non-null Kotlin property as a labelled box that must contain an object before the Activity is fully ready. Java silently puts an invisible null marker into an empty box. Kotlin does not do that for non-null types.
lateinit is a note attached to the box saying: “I will place the object in this box before anyone opens it.” If code opens the box too early, Kotlin reports an initialization error instead of quietly using null.
Syntax and Examples
Declare a property that will be initialized later with lateinit var:
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
open class BaseActivity : AppCompatActivity() {
protected lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
}
}
A subclass can use it after the base class has initialized it:
class SettingsActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val darkMode = sharedPreferences.getBoolean("dark_mode", false)
}
}
The order matters: super.onCreate(savedInstanceState) runs BaseActivity.onCreate(), which assigns sharedPreferences, before the subclass reads it.
You can check whether a lateinit property has been initialized from inside the declaring class:
if (::sharedPreferences.isInitialized) {
// It is safe to read sharedPreferences.
}
Step by Step Execution
Consider this base and child Activity:
open class BaseActivity : AppCompatActivity() {
protected lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
}
}
class HomeActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val welcomeShown = sharedPreferences.getBoolean("welcome_shown", false)
}
}
Execution proceeds as follows:
- Android creates
HomeActivity. At this point,sharedPreferenceshas no assigned value. - Android calls
HomeActivity.onCreate(). HomeActivitycallssuper.onCreate(savedInstanceState).BaseActivity.onCreate()obtains the preferences object and assigns it tosharedPreferences.- Control returns to
HomeActivity.onCreate(). sharedPreferences.getBoolean(...)is safe because the property is now initialized.
If step 3 were omitted, the base initialization would not run and accessing the property would throw UninitializedPropertyAccessException.
Real World Use Cases
Delayed property initialization is common in Android because many values are only available after an Activity, Fragment, View, or dependency container is ready.
Examples include:
SharedPreferencescreated from an AndroidContext.- View binding assigned after
setContentView()or view inflation. - A database or repository supplied during application startup.
- A
RecyclerViewadapter configured inonCreate(). - Services initialized after configuration or authentication is available.
Use lateinit when the value is required and the lifecycle guarantees assignment before every valid use.
Real Codebase Usage
In production Kotlin code, developers choose initialization style based on ownership and lifetime.
Base Activity property
A base Activity may expose a protected dependency to children:
open class BaseActivity : AppCompatActivity() {
protected lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
}
}
Prefer constructor injection outside Android lifecycle classes
For regular classes, make required dependencies available immediately:
class UserSettings(private val preferences: SharedPreferences) {
fun isNotificationsEnabled(): Boolean {
return preferences.getBoolean("notifications_enabled", true)
}
}
This avoids an object that is temporarily incomplete.
Guard a lifecycle-dependent property when necessary
If an operation can occur before initialization, check it or redesign the lifecycle:
fun clearSettingsIfReady() {
if (::sharedPreferences.isInitialized) {
sharedPreferences.edit().clear().apply()
}
}
Usually, a guard is a signal to reconsider where the method is called. Required dependencies should ideally be initialized at a predictable boundary.
Common Mistakes
Declaring a non-null property without initializing it
This does not compile:
protected var sharedPreferences: SharedPreferences
Fix it with lateinit, an initializer, lazy, or a nullable type.
Accessing a lateinit property too early
protected lateinit var sharedPreferences: SharedPreferences
fun readTheme() = sharedPreferences.getString("theme", "system")
This throws UninitializedPropertyAccessException if readTheme() runs before onCreate() assigns the property. Ensure lifecycle order, or use a safer design.
Forgetting super.onCreate() in a subclass
class HomeActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Missing super.onCreate(savedInstanceState)
sharedPreferences.getBoolean("welcome_shown", false)
}
}
The base Activity never initializes the property. Call super.onCreate(savedInstanceState) before using inherited lifecycle-initialized properties.
Using lateinit with unsupported types
lateinit cannot be used with primitive types such as Int, Boolean, or Double:
// Invalid
lateinit var retryCount: Int
Use an initial value, a nullable type, or another representation:
var retryCount: Int = 0
Making everything nullable
protected var sharedPreferences: SharedPreferences? = null
This compiles, but every access requires null handling:
val enabled = sharedPreferences?.getBoolean("enabled", false) ?: false
Use nullable properties only when null is a meaningful valid state, not merely to bypass initialization rules.
Comparisons
| Approach | Example | Best when | Main trade-off |
|---|---|---|---|
| Immediate initialization | val prefs = ... | The value is available during construction | Cannot depend on a later lifecycle phase |
lateinit var | lateinit var prefs: SharedPreferences | A required reference is assigned before use | Throws if accessed before assignment |
| Nullable property | var prefs: SharedPreferences? = null | Absence is a real, valid state | Requires null handling at each use |
| Lazy property | val prefs by lazy { ... } | Value should be created only on first use | Initialization happens at access time, not explicitly in onCreate() |
| Constructor parameter | class Service(val prefs: SharedPreferences) | Regular classes with required dependencies | Caller must provide the dependency immediately |
val means the property reference cannot be reassigned after initialization. var means it can change. Since lateinit means “assign this later,” it works only with var.
A lazy alternative can be useful in an Activity when the context is already valid at first access:
protected val sharedPreferences: SharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(this)
}
Unlike lateinit, this has no uninitialized period after the first access is requested; Kotlin runs the block automatically and stores the result.
Cheat Sheet
// Required reference, assigned later
lateinit var property: SomeReferenceType
// Check from the declaring class
if (::property.isInitialized) {
use(property)
}
// Optional value
var property: SomeReferenceType? = null
// Compute once on first access
val property: SomeReferenceType by lazy {
createProperty()
}
- A property of type
Tis non-null and needs a value. - A property of type
T?may holdnull. lateinitworks only withvarand non-null reference types.- Accessing an uninitialized
lateinitproperty throwsUninitializedPropertyAccessException. - Initialize base class lifecycle properties before subclasses use them.
- Prefer constructor parameters for required dependencies in ordinary Kotlin classes.
FAQ
Why does Kotlin require properties to be initialized?
A property declared as a non-null type, such as SharedPreferences, promises that it always has an object. Kotlin enforces that promise at compile time.
What is the Kotlin equivalent of an uninitialized Java reference field?
Use lateinit var property: Type if it will be assigned before use, or var property: Type? = null if null is a valid state.
Should I use lateinit or lazy for SharedPreferences?
Use lateinit when you deliberately initialize it in onCreate(). Use lazy when creating it on its first access is clearer and safe for your component lifecycle.
Can a lateinit property be null?
No. Its declared type is non-null. Before assignment it is uninitialized, which is different from containing null.
Can I declare lateinit val?
No. lateinit requires var because assignment happens after construction. A lazy property is commonly used when you want a read-only val initialized later.
What happens if I access lateinit before initialization?
Kotlin throws UninitializedPropertyAccessException at runtime. Ensure initialization happens before access.
Can subclasses access a protected lateinit property?
Yes. A subclass can access it, but it must call the base class initialization path first, typically through super.onCreate(savedInstanceState).
Mini Project
Description
Create a base Activity that manages an application preference and lets a child Activity read and update it. This demonstrates how lateinit supports lifecycle-based initialization while retaining a non-null property type.
Goal
Build a base Activity that initializes SharedPreferences in onCreate() and a child Activity that safely reads and writes a preference.
Requirements
- Create a
BaseActivitywith a protectedlateinitSharedPreferencesproperty. - Initialize the property in
BaseActivity.onCreate(). - Create a child Activity that calls
super.onCreate()before accessing the property. - Read a
dark_modeBoolean preference withfalseas its default value. - Save the opposite Boolean value back to preferences.
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.