What are the reason why we use or explicitly use get() and set() in Kotlin? I have a model that is throwing an error when I remove explicit get() in its variable declaration.
data class SampleDomain(
val publishTime: String = ""
) {
// Removing get() here, publishTime becomes empty
val formattedDate: String
get() = LocalDate.parse(publishTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME).format(
DateTimeFormatter.ofPattern("MMM. dd, yyyy")
)
}
As broot says, adding get() changes the meaning very significantly.
Without the get(), you're declaring a property with an initial value. For example:
class SomeClass {
var prop = initialisingFunction()
}
That's roughly equivalent to:
class SomeClass {
var prop: Int
init {
prop = initialisingFunction()
}
}
So initialisingFunction() is called only once, during construction, and the class stores its value in a backing field after that. If the property is a var, as here, then the value can be changed after that.
But with the get(), you're declaring a property with a custom getter:
class SomeClass {
val prop
get() = initialisingFunction()
}
That's even more roughly equivalent to:
class SomeClass {
fun getProp() = initialisingFunction()
}
Here, there's no backing field to store the property; every time you get its value, it will call initialisingFunction() and hand you that value directly.
If the property is a var, then you'll also need a custom setter. (Otherwise, the default one would cause a backing field to be created, which it would set, but whose value you'd never see…)
Which version to use will depend on your needs. If the property needs to change in line with something external, then a custom getter is probably needed. It can also be a slightly memory saving if you need to return a value that's the same for all instance of the class. But if calculating the value is expensive, and/or if it's different for each instance, especially if it needs to be mutable, then you'll probably need a property with an initialiser.
get() and set() is how we define getters and setters in Kotlin. So simply answering why do we use them is because they are required to define a getter/setter.
If you mean the difference between the following definitions:
val formattedDate: String = acquireDate()
val formattedDate: String get() = acquireDate()
Then get() is not here just to be more explicit. These two code fragments do much different things. The first acquires the date during the object initialization, stores it inside a field and the getter returns this stored value. The second defines a custom getter, but the value is not stored anywhere - the date is acquired again and again every time the getter is invoked.
See the documentation for more info: https://kotlinlang.org/docs/properties.html#getters-and-setters
get()
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:
class Recangle(
var width: Int = 10,
var height: Int = 20
) {
val area: Int get() = width * height
init {
println("old area: $area") // 10 * 20 = 200
width = 20
println("new area: $area") // 20 * 20 = 400
}
}
So with custom getter, every time we call area, width * height will be calculated again, let's see what will happen without custom getter:
class Recangle(
var width: Int = 10,
var height: Int = 20
) {
val area: Int = width * height // 10 * 20 = 200
init {
println("old area: $area") // 200
width = 20
println("new area: $area") // 200
}
}
Without a custom getter we will get the same result every time we call area, even if the width and the height are changed, so the area we be calculated only the first time and stay the same.
set()
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:
class Recangle() {
var area: Int = 0
set(value) {
val areaValue = value / 1000 // make custom operations
updateArea(areaValue) // use value in other functions
field = value // update the field value
}
fun updateArea(area: Int) {}
}
Also you can make setter private:
var area: Int = 100
private set // the setter is private and has the default implementation
Related
I am trying to observe the LiveData for the method which returns custom pair having 2 values. I want the observable to be triggered when I change either one of the values. But it is not getting triggered. Following is the code:
CustomPair.kt
data class CustomPair<T, V>(
var first : T,
var second : V
)
Observable:
falconViewModel.getPlanet1Name().observe(this) {
planet1.text = it.first
planet1.isEnabled = it.second
}
Getter and setter methods in ViewModel falconViewModel
private val planet1EnabledAndText = MutableLiveData<CustomPair<String, Boolean>>()
fun getPlanet1Name() : LiveData<CustomPair<String, Boolean>> {
return planet1EnabledAndText
}
fun setPlanet1Name(planetName : String, visibility : Boolean) {
planet1EnabledAndText.value?.run {
first = planetName
second = visibility
}
}
Can't we observe the value in such case? Please help what is wrong here.
It started working when I tried to set a new value of CustomPair instead of modifying the existing values in the object.
Replaced
planet1EnabledAndText.value = CustomPair(planetName, visibility)
with
planet1EnabledAndText.value?.run {
first = planetName
second = visibility
}
and it worked.
I've got following example class:
fun main(args: Array<String>) {
var car = Car("Ford", 50, 8000)
println(car.vendor)
println("Speed: ${car.speed}")
car.speed = 65 // How does it know which setter to invoke?
println("New speed: ${car.speed}")
}
class Car(vendor: String = "Unknown", speed: Int = 0, price: Int = 0) {
var vendor = vendor
get() = field
set(newVendor: String) {
field = newVendor
}
var speed = speed
get() = field
set(speed: Int) {
field = speed
}
var price = price
get() = field
set(newPrice: Int) {
field = price
}
}
When I change the speed-attribute (please see the commented line): Where does Kotlin know from, which setter-method it has actually to invoke?
There are two setter-methods within my class (speed, price) which both are named "set" and both expect an integer-value.
Is the order, in which the methods are defined, crucial?
The respective getter-/setter-methods have to be written directly after the attribute-definition? Or does it somehow work differently? If so: How?
Are the indentations just a covention? Or are the indentations needed for the compiler?
car.speed = 65 is called Property-access syntax. It is equivalent to car.setSpeed(65).
You don't have two methods named set; you've two mutable properties speed and price both of type Int. They each have corresponding getter and setter methods; in Java Beans convention, the getter for speed would be getSpeed and the setter setSpeed(Int).
See https://kotlinlang.org/docs/reference/properties.html for more details.
I want a Kotlin property that can replace the original value. For example:
var String.setString
get() = "ignorethis"
set(value) = this = value
(I'm actually using it on UBytes to add getters and setters for the upper and lower nibbles).
However, in my example above I get Variable expected. Is it possible to modify this with an extension property?
String is immutable, and you can only do that on mutable types like this:
fun main() {
val x = mutableListOf("old")
x.setString = mutableListOf("New Pro Max Extra Large Plus")
println(x)
}
var <T> MutableList<T>.setString
get() = this
set(value) = value.forEachIndexed { idx, it -> this[idx] = it } ```
Currently I'm starting to learn Kotlin. I have a property like this:
var startTime: Int
get() = {
// read value from database
}
set(value) {
// save value to database
}
Here I always read and write the value every time I use the getter and setter.
Can this property be evaluated lazy? I want to read the value once the first time I use the getter and cache it for following calls. I know that values can be lazy but I found nothing about variables. What is the correct way in Kotlin to cache this property?
Kotlin offers lazy properties (https://kotlinlang.org/docs/reference/delegated-properties.html#lazy) that are computed on first access and cached.
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
Will produce
computed!
Hello
Hello
What you need is not lazy evaluation, but a backing field:
private var _startTime: Int? = null
var startTime: Int
get() = {
if (_startTime != null) {
return _startTime!!
} else {
// read value from database and assign it to _startTime
}
}
set(value) {
_startTime = value
// save value to database
}
Alternatively you could declare _startTime as non-nullable and have an additional flag private var isStartTimeSet: Boolean = false used for checking if it has been fetched from database already.
I find it can only access backing field in the set or get.Is there any way can access backing field in other place at class?
for example.
var width:Int=0
get() {
return field*10;
}
set(value) {
field=value/10;
}
I want to access the real value but not it multiple 10
when i using c#,there are no field keyword so always need to declare a new variable to store the real data.In the previous example it's will be something look like
private var _width=0;
var width:Int
get() {
return _width*10;
}
set(value) {
_width=value/10;
}
so if i want to access real value in the class,i can just access _value.
But in kotlin,is there have someway can just access backing field without these verbose declaration?
No. Your C# example works fine in Kotlin, it's called a backing property.
Kotlin, You can use backing properties
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:
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // Type parameters are inferred
}
return _table ?: throw AssertionError("Set to null by another thread")
}
In all respects, this is just the same as in Java since access to private properties with default getters and setters is optimized so that no function call overhead is introduced.