Properties of classes
Classes in Kotlin have properties, rather than simple fields, with implicit and automatically generated getters and setters. Syntactically, calling these getters and setters looks no different than direct field access in Java.
A common source of superfluous boilerplate code in Java are getters and setters for the fields of a class:
public class Pet {
private String name;
private Person owner;
public Pet(String name, Person owner) {
this.name = name;
this.owner = owner;
}
// from here on only boilerplate code
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
}
Of course you could just set the fields name
and owner
to public
and get rid of the getters and setters.
This would remove the boilerplate code, and the access to the fields would be more elegant:
pet.name = "Tomcat";
// instead of
pet.setName("Tomcat");
While this would not be a problem in this example, especially for name
, this could quickly become one with owner
during the course of the project:
imagine, not only the Pet
should know its owner
, but a Person
should also manage a list of all its pets
.
For this you would have to write a corresponding setter:
public void setOwner(Person owner) {
this.owner = owner;
owner.pets.add(this);
}
That is done quickly.
However, it is now more time-consuming and annoying to replace all direct accesses to owner
with calls to the new setter.
While all modern IDEs offer refactorings performing that fully automatically, there is a negative connotation due to the need to touch many source files for such a small change.
In order to avoid these situations in the first place and to take into account the obligatory encapsulation in object-oriented programming, it is common in Java to work with getters and setters and to live with the boilerplate code, along with the renunciation of the elegant syntax of direct field access.
Kotlin can do it better
But Kotlin does away with this adversity.
The above Pet
class might look like this in Kotlin:
class Pet(name: String, owner: Person) {
var name : String = name
var owner : Person = owner
}
or, with constructor properties, even shorter (but less suitable for our example):
class Pet(var name: string, var owner: person)
This already looks very similar to the version from the Java example, which dispenses with the getters and setters.
To change or query the name of a pet, this is enough:
val name = pet.name
// respectively
pet.name = "Tomcat"
This too is similar to the corresponding Java code.
Suppose again that a bidirectional association between Pet
and owner
should be established, so that our above setter becomes necessary again.
This might look like this in Kotlin:
fun setOwner(owner: Person) {
this.owner = owner
owner.pets += this
}
But, unlike Java, we do not add it as a stand-alone method, but associate it directly with the corresponding owner
field, or more precisely, the owner
property:
class Pet(name: String, owner: Person) {
var name : String = name
var owner : Person = owner
set (value) {
field = value
owner.pets += this
}
}
The corresponding syntax is to note set(value)
directly below the corresponding property, followed by the body of the setter.
Within that, you can use field
to access the underlying field in order to assign it a new value, as in the example.
But we don’t gain much with this syntax, do we?
That’s right, because the actual advantage lies not in the declaration of the setter, but in its call: because that one does not change!
As before, the owner of a pet can be set by means of
pet.owner = Person("Alex Example")
and of course, the setter is still (implicitly) called!
Accesses to properties in Kotlin always look exactly the same, no matter if there is a direct and simple field access behind them or even a complex getter/setter.
This eliminates the need for later refactoring, and one can use the nicer syntax of direct field accesses over that of invoking getters and setters.
Even the notation of trivial getters and setters is completely eliminated, since the Kotlin compiler takes over: if you do not declare your own getters/setters for a property, the Kotlin compiler generates them automatically.