Kotlin field Keyword
last modified April 19, 2025
Kotlin's properties can have custom getters and setters. The field
keyword refers to the backing field of a property. This tutorial explores the
field keyword in depth with practical examples.
Basic Definitions
The field keyword in Kotlin is used in property accessors to
reference the backing field. It's only available in custom getters or setters.
The backing field stores the actual value of the property.
Basic Property with Backing Field
When you declare a property in Kotlin, the compiler automatically generates a
backing field. You can access it using field in custom accessors.
package com.zetcode
class Person {
var name: String = ""
get() = field
set(value) {
field = value
}
}
fun main() {
val p = Person()
p.name = "John Doe"
println(p.name) // Output: John Doe
}
Here we define a simple property with explicit getter and setter. The field
keyword refers to the backing field storing the actual value. This is equivalent
to the default implementation.
Custom Getter with field
You can use field in custom getters to modify the returned value
while still accessing the stored value. This allows for computed properties.
package com.zetcode
class Temperature {
var celsius: Double = 0.0
get() = field
set(value) {
field = value
}
val fahrenheit: Double
get() = field * 9/5 + 32
}
fun main() {
val temp = Temperature()
temp.celsius = 25.0
println(temp.fahrenheit) // Output: 77.0
}
This example shows a temperature class with celsius and fahrenheit properties.
The fahrenheit property is computed from celsius but still uses field
to access the stored value. Note this won't compile as shown.
Custom Setter with field
Custom setters can use field to modify values before storing them.
This is useful for validation or transformation of input values.
package com.zetcode
class User {
var age: Int = 0
set(value) {
field = if (value >= 0) value else 0
}
}
fun main() {
val user = User()
user.age = -5
println(user.age) // Output: 0
user.age = 25
println(user.age) // Output: 25
}
Here we validate the age property in the setter. Negative values are converted to
0. The field keyword is used to store the validated value in the
backing field.
Lazy Initialization with field
The field keyword is essential for lazy initialization patterns. It
allows you to check and initialize the backing field only when needed.
package com.zetcode
class ExpensiveResource {
private var _resource: String? = null
val resource: String
get() {
if (_resource == null) {
_resource = initializeResource()
}
return _resource!!
}
private fun initializeResource(): String {
println("Initializing resource...")
return "Expensive Resource Data"
}
}
fun main() {
val er = ExpensiveResource()
println(er.resource) // Output: Initializing resource... Expensive Resource Data
println(er.resource) // Output: Expensive Resource Data
}
This shows lazy initialization using a nullable backing field. The resource is only initialized when first accessed. Subsequent accesses use the cached value.
Field in Interface Properties
Interfaces can declare properties, but they can't have backing fields. The
field keyword can only be used in concrete implementations.
package com.zetcode
interface Vehicle {
val maxSpeed: Int
}
class Car : Vehicle {
override val maxSpeed: Int = 120
get() = field
}
class Bike(override val maxSpeed: Int) : Vehicle
fun main() {
val car = Car()
println(car.maxSpeed) // Output: 120
val bike = Bike(30)
println(bike.maxSpeed) // Output: 30
}
The Vehicle interface declares a property without backing field. The Car class implements it with a backing field, while Bike uses a constructor parameter. Both are valid implementations.
Field in Delegated Properties
Delegated properties use a different mechanism than backing fields, but you can
still combine them with field for more complex scenarios.
package com.zetcode
import kotlin.properties.Delegates
class UserProfile {
var username: String by Delegates.observable("") {
prop, old, new ->
println("Username changed from $old to $new")
}
var _password: String = ""
var password: String
get() = "********"
set(value) {
_password = value.hashCode().toString()
}
}
fun main() {
val profile = UserProfile()
profile.username = "john_doe" // Output: Username changed from to john_doe
profile.password = "secret"
println(profile.password) // Output: ********
}
This combines delegated properties with backing fields. The username uses delegation while password uses a custom setter with a backing field. The actual password is stored securely as a hash.
Field in Extension Properties
Extension properties cannot have backing fields, so the field
keyword is not available in them. They must use explicit getters.
package com.zetcode
val String.hasDigits: Boolean
get() = this.any { it.isDigit() }
class Counter {
var count = 0
val Int.incremented: Int
get() = this + count
}
fun main() {
println("Hello123".hasDigits) // Output: true
println("Hello".hasDigits) // Output: false
val counter = Counter()
counter.count = 5
println(10.incremented) // Output: 15
}
Extension properties can't use field as they don't have backing
fields. They must compute values based on the receiver object or other
properties.
Best Practices for Backing Fields
- Use when needed: Only use
fieldwhen you need custom accessor logic that references the stored value. - Avoid recursion: Be careful not to create infinite recursion
by using the property name instead of
field. - Consider lazy: For expensive initialization, consider using
lazydelegate instead of manual field management. - Keep simple: For simple properties, rely on automatic
backing fields without explicit
fieldusage. - Document: Document any non-trivial behavior in property
accessors that use
field.
Source
Kotlin Properties Documentation
This tutorial covered Kotlin's field keyword in depth, showing how
it provides access to property backing fields. We explored various scenarios
including custom accessors, lazy initialization, and property delegation.
Proper use of backing fields can make your property implementations more
efficient and maintainable.
Author
List all Kotlin tutorials.