Properties
Declaring properties
Properties in Kotlin classes can be declared either as mutable, using the var
keyword, or as read-only, using the val
keyword.
To use a property, simply refer to it by its name:
Getters and setters
The full syntax for declaring a property is as follows:
The initializer, getter, and setter are optional. The property type is optional if it can be inferred from the initializer or the getter's return type, as shown below:
The full syntax of a read-only property declaration differs from a mutable one in two ways: it starts with val
instead of var
and does not allow a setter:
You can define custom accessors for a property. If you define a custom getter, it will be called every time you access the property (this way you can implement a computed property). Here's an example of a custom getter:
You can omit the property type if it can be inferred from the getter:
If you define a custom setter, it will be called every time you assign a value to the property, except its initialization. A custom setter looks like this:
By convention, the name of the setter parameter is value
, but you can choose a different name if you prefer.
If you need to annotate an accessor or change its visibility, but you don't want to change the default implementation, you can define the accessor without defining its body:
Backing fields
In Kotlin, a field is only used as a part of a property to hold its value in memory. Fields cannot be declared directly. However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field
identifier:
The field
identifier can only be used in the accessors of the property.
A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a custom accessor references it through the field
identifier.
For example, there would be no backing field in the following case:
Backing properties
If you want to do something that does not fit into this implicit backing field scheme, you can always fall back to having a backing property:
Compile-time constants
If the value of a read-only property is known at compile time, mark it as a compile time constant using the const
modifier. Such a property needs to fulfill the following requirements:
It must be a top-level property, or a member of an
object
declaration or a companion object.It must be initialized with a value of type
String
or a primitive typeIt cannot be a custom getter
The compiler will inline usages of the constant, replacing the reference to the constant with its actual value. However, the field will not be removed and therefore can be interacted with using reflection.
Such properties can also be used in annotations:
Late-initialized properties and variables
Normally, properties declared as having a non-nullable type must be initialized in the constructor. However, it is often the case that doing so is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In these cases, you cannot supply a non-nullable initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
To handle such cases, you can mark the property with the lateinit
modifier:
This modifier can be used on var
properties declared inside the body of a class (not in the primary constructor, and only when the property does not have a custom getter or setter), as well as for top-level properties and local variables. The type of the property or variable must be non-nullable, and it must not be a primitive type.
Accessing a lateinit
property before it has been initialized throws a special exception that clearly identifies the property being accessed and the fact that it hasn't been initialized.
Checking whether a lateinit var is initialized
To check whether a lateinit var
has already been initialized, use .isInitialized
on the reference to that property:
This check is only available for properties that are lexically accessible when declared in the same type, in one of the outer types, or at top level in the same file.
Overriding properties
Delegated properties
The most common kind of property simply reads from (and maybe writes to) a backing field, but custom getters and setters allow you to use properties so one can implement any sort of behavior of a property. Somewhere in between the simplicity of the first kind and variety of the second, there are common patterns for what properties can do. A few examples: lazy values, reading from a map by a given key, accessing a database, notifying a listener on access.
Such common behaviors can be implemented as libraries using delegated properties.