As a Java developer, the concept of a backing field is a bit foreign to me. Given:
class Sample {
var counter = 0 // the initializer value is written directly
Initially, I too had a tough time understanding this concept. So let me explain it to you with the help of an example.
Consider this Kotlin class
class DummyClass {
var size = 0;
var isEmpty
get() = size == 0
set(value) {
size = size * 2
}
}
Now when we look at the code, we can see that it has 2 properties i.e - size
(with default accessors) and isEmpty
(with custom accessors). But it has only 1 field i.e. size
. To understand that it has only 1 field, let us see the Java equivalent of this class.
Go to Tools -> Kotlin -> Show Kotlin ByteCode in Android Studio. Click on Decompile.
public final class DummyClass {
private int size;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.size == 0;
}
public final void setEmpty(boolean value) {
this.size *= 2;
}
}
Clearly we can see that the java class has only getter and setter functions for isEmpty
, and there is no field declared for it. Similarly in Kotlin, there is no backing field for property isEmpty
, since the property doesn't depend on that field at all. Thus no backing field.
Now let us remove the custom getter and setter of isEmpty
property.
class DummyClass {
var size = 0;
var isEmpty = false
}
And the Java equivalent of the above class is
public final class DummyClass {
private int size;
private boolean isEmpty;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.isEmpty;
}
public final void setEmpty(boolean var1) {
this.isEmpty = var1;
}
}
Here we see both the fields size
and isEmpty
. isEmpty
is a backing field because the getter and setter for isEmpty
property depend upon it.
My understanding is using field identifier as a reference to the property's value in get or set, when you want to change or use the property's value in get or set.
For example:
class A{
var a:Int=1
get(){return field * 2} // Similiar to Java: public int geta(){return this.a * 2}
set(value) {field = value + 1}
}
Then:
var t = A()
println(t.a) // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2 // The real action is similar to Java code: t.a = t.a +1
println(t.a) // OUTPUT: 6, equal to Java code: println(t.a * 2)
Backing fields are good for running validation or triggering events on state change. Think of the times you've added code to a Java setter/getter. Backing fields would be useful in similar scenarios. You would use backing fields when you needed to control or have visibility over setters/getters.
When assigning the field with the field name itself, you're actually invoking the setter (i.e. set(value)
). In the example you have, this.counter = value
would recurse into set(value) until we overflow our stack. Using field
bypasses the setter (or getter) code.
The terminology backing field
is filled with mystery. The keyword used is field
. The get/set
methods, follows immediately next to the member variable that is about to be get or set through this door protective methods mechanism. The field
keyword just refers to the member variable that is to be set or get. At present Kotlin, you cannot refer to the member variable directly inside the get or set protective door methods because it will unfortunately result to infinite recursion because it will re-invoke the get or set and thus leds the runtime down into the deep abyss.
In C# though, you can directly reference the member variable inside the getter/setter methods. I am citing this comparison to present the idea that this field
keyword is how the present Kotlin is implementing it but I do hope it will be removed in later versions and allow us to directly reference the member variable directly without resulting to infinite recursion.
Because, say if you don't have field
keyword, you won't be able to actually set/get the value in the get()
or set(value)
. It enables you to access the backing field in the custom accessors.
This is the equivalent Java code of your sample:
class Sample {
private int counter = 0;
public void setCounter(int value) {
if (value >= 0) setCounter(value);
}
public int getCounter() {
return counter;
}
}
Apparently this is not good, as the setter is just an infinte recursion into itself, never changing anything. Remember in kotlin whenever you write foo.bar = value
it will be translated into a setter call instead of a PUTFIELD
.
EDIT: Java has fields while Kotlin has properties, which is a rather higher level concept than fields.
There are two types of properties: one with a backing field, one without.
A property with a backing field will store the value in the form of a field. That field makes storing value in memory possible. An example of such property is the first
and second
properties of Pair
. That property will change the in-memory representation of Pair
.
A property without a backing field will have to store their value in other ways than directly storing it in memory. It must be computed from other properties, or, the object itself. An example of such property is the indices
extension property of List
, which is not backed by a field, but a computed result based on size
property. So it won't change the in-memory representation of List
(which it can't do at all because Java is statically typed).