Understand Fields and Properties in Kotlin
How Kotlin implicitly implements field, getter and setter function for you when you declare a property?
Properties and fields terminologies in Kotlin sometimes is a bit confusing because technically, Kotlin doesn't have Fields. You can't declare a field. Everything is Properties!
However, to avoid confusion, I prefer to define Fields and Properties separately based on the following:
- Fields are private member variables of a class. Memory is allocated.
- Properties are public or protected getter or setter functions which allow you to access to the private fields.
I like to define like this because it helps my understanding and it also makes things a lot easier to explain.
Implicit Field, Implicit Getter/Setter
Let's look at this example. name
is a property.
class Person {
var name = "Vincent"
}
When you declare a property like this, Kotlin implicitly creates field, getter and setter functions for you.
In Java decompiled code, it looks like this:
public final class Person {
@NotNull
private String name = "Vincent";
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
}
}
As you can see, private String name
is the field (member variable). getName()
and setName()
are the property getter and setter functions (also known as property accessors)
Implicit Field, Explicit Getter/Setter
Of course, you can also explicitly define the property getter and setter functions, which also generates a very similar decompiled Java code.
class Person {
var name: String = "Vincent"
get() { return field }
set(value) { field = value }
}
field
is implicitly created here, which is also called Backing Fields. Providing property accessors (i.e. get()
and set()
) to the property is called Backing Properties.
Explicit Field, Explicit Getter/Setter
You can explicitly define Field too. Basically everything is explicit now.
class Person {
private var _name:String = "Vincent"
var name: String
get() { return _name }
set(value) { _name = value }
}
Instead of implicit
field
,_name
is the explicit field here.
Private Set or Backing Properties?
Now, you want the property name
to be read only outside the class. So you can restrict the property setter using private set
.
For example:
class Person {
var name: String = "Vincent"
private set
}
Or you can also use Backing Properties. Remove the set()
and change the var
to val
.
class Person {
private var _name:String = "Vincent"
val name: String
get() { return _name }
}
Both of the code generate the same decompile Java code as below. Please note, the setName()
function removed.
public final class Person {
@NotNull
private String name = "Vincent";
@NotNull
public final String getName() {
return this.name;
}
}
I personally prefer private set
because it has less code.
Misuse of private set
But wait, not so fast. What if you convert the following backing property
class MainViewModel: ViewModel() {
private val _state: MutableState<Int?> = mutableStateOf(null)
val state: State<Int?> = _state
/*...*/
}
to private set
class MainViewModel: ViewModel() {
var state: MutableState<Int?> = mutableStateOf(null)
private set
}
This is a very good example of misusing the private set
. What it really means is you can't assign a new variable to state
outside the MainViewModel
class. The state
variable itself is still mutable (which means you can modify its value).
The backing property above exposes only the read only State
, changing it to private set
defeats its original purpose. So, in this scenario, you don't use the private set
. This applies to any mutable data.
Conclusion
I think it is important to understand the fields and properties concepts here.
When you declare a property, it doesn't allocate a new memory because it is merely a getter or setter function. However, if implicit field implementation is inferred (like code examples above), then yes, it takes up the memory allocation.
Finally, don't convert every backing property to private set
. You shouldn't do that, especially your data is mutable.