I started playing arround with Kotlin and read something about mutable val with custom getter. As refered in e.g here or in the Kotlin Coding Convention one should not override the getter if the result can change.
class SampleArray(val size: Int) {
val isEmpty get() = size == 0 // size is set at the beginning and does not change so this is ok
}
class SampleArray(var size: Int) {
fun isEmpty() { return size == 0 } // size is set at the beginning but can also change over time so function is prefered
}
But just from the perspective of usage as in the guidelines where is the difference between the following two
class SampleArray(val size: Int) {
val isEmpty get() = size == 0 // size can not change so this can be used instad of function
val isEmpty = size == 0 // isEmpty is assigned at the beginning ad will keep this value also if size could change
}
From this answer I could see that for getter override the value is not stored. Is there something else where the getter override is different form the assignment? Maybe with delegates or latinit?
In your second example size is immutable value and therefore both ways are valid.
However variant with overridden getter get() = size == 0 has no backing field and therefore size == 0 is evaluated every time you access isEmpty variable.
On the other hand, when using initializer = size == 0 the expression size == 0 is evaluated during construction (check exactly when and how here - An in-depth look at Kotlin’s initializers) and stored to the backing field, of which value is then returned when you access the variable.
The key difference here is that in val isEmpty get() = ... the body is evaluated each time the property is accessed, and in val isEmpty = ... the expression on the right hand side gets evaluated during the object construction, the result is stored in the backing field and this exact result is returned each time the property is used.
So, the first approach is suitable when you want the result to be calculated each time, while the second approach is good when you want the result to be calculated only once and stored.
Related
My expectation:
I want to see something like that:
package com.example.myapplication
class ExampleGet {
val p2: String = "Black"
}
fun main(){
var ex = ExampleGet()
println(ex.p2)
}
I understand this example, it's work fine.
My problem
I don't know why do we need a word get in this class
package com.example.myapplication
class ExampleGet {
val p: String get() = "Black"
val p2: String = "Black"
}
fun main(){
var ex = ExampleGet()
println(ex.p)
println(ex.p2)
println(ex.p==ex.p2)
}
But I don't know what's difference between
Line 1
val p: String get() = "Black"
and
Line 2
val p2: String = "Black"
If we don't have any difference between Line 1 and Line 2 why do get() exist in kotlin? I ask because I have fond an example
package com.example.myapplication
import androidx.fragment.app.Fragment
import com.example.myapplication.databinding.FragmentThirdBinding
class ThirdFragment:Fragment() {
private var _binding : FragmentThirdBinding? = null
private val binding get() = _binding!!
}
I don't know why did people use
private val binding get() = _binding!!
but not
private val binding = _binding!!
Properties in Kotlin can have an initializer, a getter, and a setter, but all of them are optional.
When you write
val p2: String = "black"
the property p2 is initialized with value "black". It has an implicit getter that always returns the current value of the property, and it would have an implicit setter that sets that value, if it was a var and not a val.
When you write
val p: String get() = "black"
you defined an explicit getter for the property p that always returns "black". So, in this example it does not become clear what the difference is, because "black" is a constant value.
Let's consider instead the following example:
val p1 : String = System.nanoTime()
val p2 : String get() = System.nanoTime()
When you use property p1, it will always return the time in nanoseconds of the moment it was initialized.
However, when you use property p2, it will always return the time in nanoseconds of the moment, you are calling p2.
So, regarding your example with the property binding, the definition with getter instead of an initializer, allows to always get the value of the internal variable _binding instead of only its initial value. The variable _binding is called a backing property.
Short answer: both lines define a property, but one has an initialiser while the other overrides the getter method.
In Kotlin, a property always has a getter method (and, if it's mutable, a setter method). When you refer to the property (e.g. myExampleGet.p), you're actually calling the getter method. (This is unlike Java.) A property will usually (though not always) have a private field to store the value, as well (known as the ‘backing field’).
Let's take your two cases in reverse order. Your second case has an initialiser, which is the most common form:
val p2: String = "Black"
This defines a property called p2, of type String, and assigns it the initial value "Black". You don't specify a setter method, so you get the default one, which just returns the backing field.
Your first case provides a setter method, instead of an initialiser:
val p: String get() = "Black"
This says that p is a property with type String, and that its getter method always returns the hard-coded value "Black".
This property doesn't need a backing field, because it would never be used.
So, what's the difference? In your example, very little. The main one is that every instance of ExampleGet has a field called p2, all of which contain the same reference (to the hard-coded string); they do not have a field p. So p is more memory-efficient.
However, in other situations, the difference is much less subtle! For example, the setter might not return a constant value, e.g.:
class ExampleGet {
val p: String get() = (++counter).toString()
val p2: String = (++counter).toString()
private var counter = 0
}
Here p2 would always have the same value it was initialised with (probably "1"), while p would give a different value each time: "2", then "3", then "4", and so on. (In practice, such a getter might perform some calculation on another property, or get it from some other source, but I hope this illustrates the point.)
Another situation making the difference obvious would be if the properties were mutable, i.e. var instead of val:
class ExampleGet {
var p: String get() = "Black"
var p2: String = "Black"
}
Here p2 would behave as you expect, returning the value you set:
val eg = ExampleGet()
println(eg.p2) // prints "Black"
eg.p2 = "White"
println(eg.p2) // prints "White"
But p would always return the same value:
eg.p = "White"
println(eg.p) // prints "Black"
(I think p would have a backing field in this case, which would store whatever value you set — but you'd never be able to see that value, because the setter would always return the hard-coded value.)
So the two cases are doing very different things, even though the effect is practically the same in the code in the question.
The difference you can see in decompiled Kotlin into Java code
So the code:
class ExampleGet {
val p: String get() = "Black"
val p2: String = "Black"
}
Become (Java):
public final class ExampleGet {
#NotNull
private final String p2 = "Black";
#NotNull
public final String getP() {
return "Black";
}
#NotNull
public final String getP2() {
return this.p2;
}
}
As you can see, val with get() become method returning value.
In my practice, I use variable shadowing to make user's code operate with different type, for example:
val publicValue: List<String>
get() = _privateValue
private val _privateValue: MutableList<String>...
It's been covered, but specifically for this stuff in your example:
private var _binding : FragmentThirdBinding? = null
private val binding: FragmentThirdBinding get() = _binding!!
I've been explicit about the types here - _binding is nullable, binding is non-null. binding is a getter that's casting the value of _binding to another type (the non-null equivalent). When people access binding, they "don't have to worry" about it being null, don't have to do any null handling etc.
Here's the thing - none of that makes any sense. It's only non-null because they've asserted that with the !! operator (which should generally be seen as a red flag - it's circumventing the nullability checker, telling it it's wrong).
What they're probably doing is assigning binding later (e.g. in onCreate), but the variable needs to be initialised to something before that, so they make it nullable and set it to null as a placeholder. But that makes binding nullable, and it needs to be null-checked every time, even if in reality, it will always have been assigned to something by then, and will never be null when something tries to use it.
So this solution creates another placeholder variable, called _binding, which is nullable. But the code accesses binding instead, which is non-null. It's all based on the idea that _binding definitely won't be null when accessed, so the !! will always be valid.
This situation is what lateinit is for though:
lateinit var binding: FragmentThirdBinding
Same thing - a promise to assign it before it's read, no need for nullability. It's a var instead of a val but that's rarely going to matter, and not for something like this where you're only going to set it once anyway. To me it's more readable, uses the language features instead of working around them (!!), etc.
I'm not sure where the "cast a nullable field" pattern came from, it looks a lot like the way you're recommended to expose a private MutableLiveData as a public immutable LiveData type instead, so I'm not sure if people are just adapting that. Maybe there's a benefit to it I don't know about!
Say I have a data class
data class MyClass(val crop: Rect, val name: String)
But I want to make a copy of the Rect passed in since I don't want the value to be modified later. I don't want to the caller to call
MyClass(Rect(inCrop), "name")
in the code. How can I do this in my data class?
Thanks.
One workaround I can think of is:
data class MyClass(private var privateCrop: Rect, val name: String) {
val crop get() = privateCrop
init {
privateCrop = Rect(privateCrop)
}
}
You make crop private and make it a var (privateCrop), then you add a public getter for it. Now you can copy it in an init block.
But I gotta admit, this is rather ugly. The better solution here I think is to change Rect to be immutable, but if Rect isn't in your control, then I guess it can't be helped. You might also consider using a regular class.
You may not want to alter data class's like this. As per another solution's answer, you may find other peculiarities with this solution. The solution given by #Sweeper, also does not include providing a defensive copy, which you may want to do to avoid access to modifying the internal property field.
To quote:
After spending almost a full year of writing Kotlin daily I've found that attempting to override data classes like this is a bad practice. There are 3 valid approaches to this, and after I present them, I'll explain why the approach other answers have suggested is bad.
Have your business logic that creates the data class alter the value to be 0 or greater before calling the constructor with the bad value. This is probably the best approach for most cases.
Don't use a data class. Use a regular class and have your IDE generate the equals and hashCode methods for you (or don't, if you don't need them). Yes, you'll have to re-generate it if any of the properties are changed on the object, but you are left with total control of the object.
class Test(value: Int) {
val value: Int = value
get() = if (field < 0) 0 else field
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Test) return false
return true
}
override fun hashCode(): Int {
return javaClass.hashCode()
}
}
Create an additional safe property on the object that does what you want instead of having a private value that's effectively overriden.
I have read that using !! should generally be avoided.
Is there a way to write the following code in a more elegant way without having to add something like obsolete null checks and duplicated or dead blocks of code?
class A(var field: Thing?) {
fun getField(): Thing {
if (field == null) {
field = Thing()
}
return field!!
}
}
Also I don't understand why the compiler requires the !!-'pray-this-isn't-null-operator' to be satisfied in this scenario.
EDIT: Consider that it is important to me that a potential solution uses lazy initialization if the field is null!
Problem
As Enzokie already mentioned in the comments, another thread could have changed field after the null check. The compiler has no way of knowing that, so you have to tell it.
class A(var field: Thing?) {
fun getField(): Thing {
if (field == null) {
field = Thing()
}
// another thread could have assigned null to field
return field!! // tell the compiler: I am sure that did not happen
}
}
Solution (Eager)
In you particular case it would be a good idea to use a parameter f (you could name it "field" too, but I avoided that for clarity) in the constructor (without val/var) and afterwards assign it to a property field to which you assign either f or a new instance of Thing.
This can be expressed really concise with the Elvis operator :? which takes the left hand side if not null and the right hand side of the expression otherwise. So, in the end field will be of type Thing.
class A(f: Thing?) {
val field = f ?: Thing() // inferred type Thing
}
Solution (Lazy)
Since it was mentioned by gidds, if you need to initialize field lazyly you could do it like this using delegated properties:
class A(f: Thing?) {
val field by lazy {
f ?: Thing() // inferred type Thing
}
}
The call site does not change:
val a = A(null) // field won't be initialized after this line...
a.field // ... but after this
How about this?
class A(field: Thing?) {
private lateinit var field: Thing
init {
field?.let { this.field = it }
}
fun getField(): Thing {
if (!this::field.isInitialized) {
field = Thing()
}
return field
}
}
When you define a field, you actually define a variable plus two accessor methods:
val counter: Integer = 0
It is possible to customize the accessor methods by writing this instead:
val n = 0
val counter: Integer
get() = n++
This will execute the n++ each time you access the counter field, which therefore returns different values on each access. It is uncommon and unexpected but technically possible.
Therefore the Kotlin compiler cannot assume that two accesses to the same field return the same value twice. Usually they do, but it is not guaranteed.
To work around this, you can read the field once by copying it into a local variable:
fun count() {
val counter = counter
println("The counter is $counter, and it is still $counter.")
}
My goal: I have a simple class with a public
val reds = IntArray(10)
val greens = IntArray(10)
val blues = IntArray(10)
val lums = IntArray(10)
If someone modifies any red value, I'd like to update the lum value.
myObj.reds[5] = 100 // Should update myObj.lums[5] = reds[5]+greens[5]+blues[5]
The problems is that the by Delegates.observable seem to only be used for var objects - nothing mentions "and if you modify an element of an array, here is what gets triggered"
Maybe this isn't possible and I have to do all modifications through getters and setters - but I'd much rather have something trigger like an observable!
You will have to use a custom class instead, IntArray is mapped to primitive int[] array so it doesn't provide any place to inject callback - changing value like your example (myObj.reds[5] = 100) you only know when array is returned, but have no control over changes after that.
For example you can create class like this:
class IntArrayWrapper(size: Int,
val setterObs : ((index: Int, value: Int) -> Unit)? = null){
val field = IntArray(size)
val size
get() = field.size
operator fun iterator() = field.iterator()
operator fun get(i: Int) : Int {
return field[i]
}
operator fun set(i: Int, value: Int){
field[i] = value
setterObs?.invoke(i, value)
}
}
Operator functions will let you get values from underlying array with same syntax as if you were accessing it directly. setterObs argument in constructor lets you pass the "observer" for setter method:
val reds = IntArrayWrapper(10){index, value ->
println("$index changed to $value")
invalidateLums(index) // method that modifies lums or whatever you need
}
val a = reds[2] // getter usage
reds[3] = 5 // setter usage that triggers the setter observer callback
reds.field[4] = 3 // set value in backing array directly, allows modification without setter callback
Note that this imposes limitations, as you won't be able to freely use IntArray extension methods without referencing backing field nor will you be able to pass this class as an Array argument.
I do not know if it is the cleanest way of solving your problem but, you could use the ObservableList (FX observable collections):
var numbers: ObservableList<Int> = FXCollections.observableArrayList()
numbers.addListener(ListChangeListener<Int> {
//Do things on change
})
But as I mentioned, by adding these collections you are mixing FX components into your application, which I do not know if it is wanted or even if it works on various platforms like android!
Are these equivalent?
val foo = someFooReturningFunction()
val foo get() = someFooReturningFunction()
The way I understood the documentation they were, but in my own testing they are not.
With the get() someFooReturningFunction() is evaluated each time the property is accessed, without it is only evaluated once.
They are not equivalent. The custom getter is indeed evaluated on each property access, similarly to a normal function, while a val property with no custom accessors is only evaluated once on initialization (and is actually stored in a final field on JVM platform).
Here are at least a few more differences:
The control flow analysis and nullability inference takes it into account if a property has a custom getter (or is open and thus might be overridden with a custom getter), because there's no guarantee that the property returns the same value on successive calls:
if (someObject.defaultGetterProperty != null) {
someObject.defaultGetterProperty.let { println(it) } // OK
}
if (someObject.propertyWithCustomGetter != null) {
someObject.propertyWithCustomGetter { println(it) } // Error: cannot smart-cast
}
When a property is private, if it has no custom getter then the getter is not generated at all and the backing field is accessed directly. This, however, is an implementation detail and not something to rely on.
No. In addition to #hotkey's reasons, here's a simple demonstration using mutable properties showing when they're definitely not equivalent. TLDR: if your property is calculated using a mutable property, always use a custom getter over an initializer.
data class Calculation(val value1: Int, var value2: Int) {
val sum: Int = value1 + value2
val sumWithGetter: Int
get() = value1 + value2
}
val calculation = Calculation(1, 2)
println(calculation.sumWithGetter) // prints 3
println(calculation.sum) // prints 3
calculation.value2 = 0
println(calculation.sumWithGetter) // prints 1 (correct)
println(calculation.sum) // prints 3!