In the short time I’ve been learning Kotlin, I have seen this quite a lot. Is this common practice even for professional and experienced Kotlin developers?
Well, time for a history lesson!
Basically, the Android world (and every Java developer until records are a thing) have been stuck with needing to use the private field with getter and setter methods pattern for quite some time. When Kotlin came around, the team wanted to massively remove boilerplate, and so they had that pattern built into the language and automatically generated. And so whenever you write this:
class Example {
val myAmazingValue: String = ""
}
Under the hood Kotlin compiles it down to this Java code:
public class Example {
private String _myAmazingValue = ""
public String getMyAmazingValue() {
return _myAmazingValue
}
public void setMyAmazingValue(String value) {
_myAmazingValue = value
}
}
Now this might seem like a performance problem, right? Wrong. method calls have very little overhead on the JVM, and whenever the JIT compiler notices a very small getter or setter like that, it just inlines it automatically. That’s because this pattern is heavily used on the JVM, and so in reality Kotlin doesn’t add any overhead at all by doing that. (Fun fact, you can actually define a property as being a public field instead of the getter/setter combination by annotating it with @JvmField
, this is provided for compatibility reasons, but most of the time you should go with the default behavior.)
This, when combined with data class
es, makes it incredibly easy to define a POJO (a POKO? idk lol) that automatically has all the getters and setters that you want. This also allows you to easily add getters and setters to any properties later on, because virtually every Kotlin library and consumer calls getters and setters. This also makes the syntax quite pleasant when working with properties.
Okay, so how do I access the attributes then?
I copy/pasted your code and initialized Example, but there are no getMyAmazingValue or setMyAmazingValue methods.
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myExample = Example() myExample.myAmazingValue = "123" }
}
class Example {
var myAmazingValue: String = “”
}
Instead i can access the attribute directly. Is this the way to do it? Or am I missing something?
Yeah I’m sorry I didn’t clarify more. myExample.myAmazingValue = "123"
will translate to myExample.setMyAmazingValue("123")
in the resulting compiled java bytecode. Furthermore, System.out.println(myExample.myAmazingValue)
will translate into System.out.println(myExample.getMyAmazingValue())
in the java bytecode. The getter and setter methods are only visible from java to preserve compatibility, but there’s no reason whatsoever to use them in Kotlin, and so they are hidden and instead you just use the normal property access syntax.
Although it looks like you are accessing the attribute directly, it’s just syntatic sugar for the actual method call, and so if myAmazingValue
had a custom getter, then that custom getter will get called (i.e. you wouldn’t directly get the value from the private field _myAmazingValue
, you’ll get the value from the function getMyAmazingValue
.)
Wow, thats actually amazing. Thanks a lot for your explanation. But then what is the difference between a public attribute and a private one?
Will those getter and setter only be automatically generated for public attributes of a class?
They’ll be generated for any property of a class, unless it is annotated by @JvmField
, regardless of the visibility. Btw, in the Kotlin world, attributes are called properties, which fits very well because properties in a lot of programming languages (like C#) refer to that combination of a storage variable and accessor methods
In fact, the only necessary part is the getter method.
If you define a simple property, e.g.:
val myField = 1
…then the compiler will generate a backing field which is private
(unless overridden by an annotation), and a getter method of the relevant visibility (in this case public
). If it were a var
, it would have a setter method too.
But you can define your own accessor method(s) instead, e.g.:
val myField = 1
get() = if (overridden) 0 else field
Within the accessor, field
refers to the backing field.
You could even write accessor(s) that don’t refer to the backing field, e.g.:
val myField: Int
get() = someOtherField * 2
In this case, the compiler doesn’t generate a backing field at all; just the accessor method(s).
That’s what allows you to write extension properties. You can’t add state to an existing class; but you can effectively add behaviour (as in extension functions), and this can also apply to properties, as long as there’s no backing field. For example:
val Int.reciprocal
get() = 1.0f / this
This defines an extension property to the Int
type. So wherever that’s in scope, you can type e.g. println(1.reciprocal)
just as if Int
s had a new field.
So a Kotlin property is implemented in the traditional Java way: it always has a getter method, and may also have a setter method (if it’s a var
) and/or a backing field
(if that’s needed) — giving you flexibility and interoperability. But at the language level, it looks like a field, with simple syntax and readability. Best of both worlds!
Thanks again for your explanation, and sorry but I have to ask again.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myExample = Example()
myExample.myAmazingValue = "123" }
}
class Example {
private var myAmazingValue: String = ""
}
I have this coding. Now when I try to access myAmazingValue from outside the Example class, I get the message "Cannot access the ‘myAmazingValue’: it is private in ‘Example’. But there is also no getmyAmazingValue( ) method or anything that I could call. Above you said that even for private properties getter and setter methods are created and hidden. Are their visibility also private and thats why I can’t access them? If so, what is the point of those? From my understanding, getter and setter methods are for external access and not from within the class.
This confuses me. Iam coming from ABAP (SAP) and it is very common to have private/protected attributes with public get/set methods. When do you use public properties and when do you use private ones? Are there any security concerns? Or is it generally okay to leave most, if not all my properties public?
Sorry if I seem a little dumb, but developing in Kotlin is just sooo different from developing in ABAP
The reason you can’t access myAmazingValue
is that its getter
is private.
You can make control the visibility of your getters and setters like this:
class Example {
var myAmazingValue: String
private get() = ""
protected set
}
In Java, it’s practically always good practice to use getters and setters. There are acceptions where you could save some boiler by not writing getters/setters and be fine. For example, if you discover you need to make an internal field use a getter after it’s already been used as a field, the change is trivial since there are no consumers you have to worry about breaking with an API change.
At this point, if we were designing our own language, we might ask,
“If all public fields need getters/setters, and the only downside for them is extra boilerplate for internal fields–and even then you still might be using them for a lot of internal fields that need calculation… If we remove the boilerplate there’s no reason to not use getters/setters for everything and then why not just get ride of fields entirely? Instead of the concept of a “field”, we’ll just say that every variable has a getter/setter and remove the need to wrap variables with accessors”
And voila! We’ve invented properties. An added bonus is that there’s no longer a method call like example.setMyAmazingValue(5)
instead everything looks like direct member access example.myAmazingValue = 5
One of my favorite ways to learn more about a language feature is to see how different languages apply that feature. For properties it’s helpful to see how C#, Swift, and Groovy handle them in addition to Kotlin and Java.
Groovy, like Kotlin, is made to interop with Java. In Groovy, if a class has a get and set method named as you would expect, it allows you to call example.myAmazingValue = 5
. Kotlin also does this for Java get/set methods in the same way.
Unlike Kotlin, Groovy does not have a way to declare a property. This means that you still have to create get/set methods as you would in Java–you to declare fields then wrap them with accessors.
Kotlin gets ride of the Java concept of fields* (excluding @JvmField and a few technicalities like const). C# and Swift do the same. Because of this, we kind of flip our way of thinking: Instead of starting with a field and wrapping it with accessors (getters/setters), we start with a property and delegate to a “backing field”. Which is the same result but the value comes from being able to consider properties as part of the public API, which usually isn’t possible with fields.
Here’s an example of using a backing field:
// No backing field, but private setter
class Example {
var myAmazingValue = 5
private set
}
// Using a plain old getter to delegate to a backing field
class Example2 {
private var _myAmazingValue: Int = 5 // This is a "backing field"... It's also a property but semantics ¯\_(ツ)_/¯
val myAmazingValue get() = _myAmazingValue
}
// Using property delegation to delegate to a backing field
class Example3 {
private var _myAmazingValue: Int = 5
val myAmazingValue by ::_myAmazingValue
}
Here’s a link to the reference page on properties: https://kotlinlang.org/docs/reference/properties.html
There’s a lot of good content in the reference
Okay, I think I got it.
Lets assume I have two classes:
class User{
var uid: Int = 0
var name: String = ""
var email: String = ""
fun login(){}
fun logout(){}
fun deleteAccount(): Boolean{ return true }
}
class UserManager{
private val newUser = User()
fun createUser(){
newUser.uid = 1
newUser.name = "First User"
newUser.email = "first@user.de"
}
fun deleteUser(uid: Int = 0) {
}
}
Would it be okay to have these type of properties defined as public, both from a performance and a security standpoint?
TL/DR: Whenever I need access to properties outside of the class, always make them public and use them directly by calling the property like Object.propertyname = “TEST”. If I dont need to access them from outside of the class, keep them private/protected.
Really appreciate all the effort you guys have put into explaining this to me!
Yup! You got it.
It’s nice to not worry about hiding all of your fields behind accessor methods since those accessor methods are built into every property
And I would like to add a nice flavor to property mechanism:
class MyClass {
var variableReadOnlyFromOutside : String = ""
private set
fun someFunctionAlteringField() {
variableReadOnlyFromOutside = "set"
}
}
val myClass : MyClass()
println(myClass.variableReadOnlyFromOutside) // Valid
myClass.variableReadOnlyFromOutside = "not allowed" // Invalid: cause compile time error
So one can easily make a externally read-only, but internally modifiable property.