Kotlin properties may look similar to Java fields, but they have different concepts.
// Kotlin property
var name: String? = null
// Java field
String name = null;
Even if you use them in the same way, Kotlin properties can do much more.
Full syntax for property declaration
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
Custom Get, Set (var)
var name: String? = null
get() = field?.toUpperCase()
set(value) { if(!value.isNullOrBlank()) { field = value } }
Custom Get (val)
val fullName: String?
get() = "$name $surname"
Backing Fields
Fields cannot be declared customly. However, Kotlin automatically generates a backing field if the property requires one. This backing field can be accessed in accessors using the `field` identifier.
var counter = 0 // the initializer assigns the backing field directly
set(value) {
if (value >= 0)
field = value
// counter = value // ERROR StackOverflow: Using actual name 'counter' would make setter recursive
}
Different from functions?
In Kotlin, if a property represents the state of an object, a function represents the behavior of that object.
// property
val isEmpty: Boolean
get() = amount == 0
// function
fun isEmpty(): Boolean {
return amount == 0
}
Let's think about it.
Is it always better to use properties?
class FruitBucket(
val price: Int,
val discount: Int,
var amount: Int
) {
val isEmpty: Boolean
get() = amount == 0
val salePrice: Int
get() = price - discount
}
The code above can be improved as follows:
Because calculating it every time it is accessed is inefficient.
class FruitBucket(
val price: Int,
val discount: Int,
var amount: Int
) {
val isEmpty: Boolean
get() = amount == 0
val salePrice: Int = price - discount
}
You can use properties without holding data.
For some reason, even if you cannot use the `Date` type, you can use Kotlin's property features to work with `Date` values by using other values instead of storing actual `Date` type data (e.g., when serializing, you might hold only the `millis` value and convert it to `Date` format when used).
var date: Date
get() = Date(millis)
set(value) {
millis = value.time
}
Properties can be used as accessors, not just as simple fields.
val Context.preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(this)
val Context.inflater: LayoutInflater
get() = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val Context.notificationManager: NotificationManager
get() = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
Are properties a silver bullet?
Properties that contain specific logic such as loops, recursion, or algorithmic parts are not the correct way to use them.
// Don't do this!
val Tree<Int>.sum: Int
get() = when (this) {
is Leaf -> value
is Node -> left.sum + right.sum
}
Properties should generally only be used to represent or set state.
Here are examples where using a function is recommended over using a property:
- When the complexity is higher than O(1) (i.e., calculation is computationally expensive)
- When it contains business logic beyond simple operations (e.g., logging, updating elements)
- When calling the member twice in a row produces different results
- When it duplicates conventional expressions like `Int.toDouble()`
- When the getter changes the state of the property
References
- https://kotlinlang.org/docs/properties.html#backing-fields
- Effective Kotlin