
Moving forward with Kotlin #1: Properties
This is part of a series of articles. You can find the rest here.
Most of us, who are lucky enough to be able to work with Kotlin often come from other programming languages. The vast majority also come from the beautiful and verbose world of Java.
However, things are different on the other side, and it’s not uncommon to fall for the convenience of bringing our old baggage with us because we are used to it and feels comfortable.
We don’t have to. We can also think of it as moving to a new home. Packing all our old stuff and, in the process, getting rid of clutter we didn’t even realise we had at the back of that dusty shelf.
Let’s have a look, in this series of articles, at some of the things that could help you find extra space in your new home in the Kotlin neighbourhood.
Properties
With Java, in-memory data “storage” comes in four shapes: variables, parameters, fields, and constants, in order of scope.
With Kotlin, parameters are similar with a few variations. They are final (so they cannot be assigned a new value), can have a default value (so we can omit them when calling it) and their names matter (as we can use them when invoking).
Now, for the other three, things are more different. As in, they are all defined as properties. Remember: we are in a different paradigm now. When we use the val
and var
keywords, we are defining properties. Not variables. Not fields. Not Constants. Just properties.
So, what is a property? We can define Properties as the characteristics of an object. They are always obtainable, we can read them with a getter, and sometimes they also accept changes, we can change them when they have a setter defined.
In the Java world, we have the concept of Beans, which contains properties. The convention here is to provide a getXxx
(or isXxx
) for each of them and a setXxx
if we want them to be writable. Enforcing greater encapsulation of information inside objects. In Kotlin, as in many other languages, we have the concept of properties embedded in the language itself.
Kotlin properties
To understand how Koltin defines properties, we have to think of them as one or two functions and not as simple data references. That said, under the hood, the compiler makes optimisations that may inline such functions when they are not required.
When we assign a val
with some data:
We are creating a property with a backing field behind it. To all effects, we can consider it something like this written in Java:
At first, it feels strange and dirty to have a property like this on an object and to access it directly. We’ve programmed ourselves to think of it as a bad practice. Repeat with me: “We must always keep fields private and have an accessor method”. Well, yeah, in Java it is a bad practice.
However, why do we do this? The reason for this hard rule is because we want to keep that value encapsulated if we later want to change the way in which we obtain the final value.
Well, with Kotlin properties, we can still do that. We know that properties are just functions to read or write data. So at any point, we can override how we retrieve the value of the property. There are several ways to do this. We can add a getter to our property:
Here we have the actual value accessible as a field
inside of the getter (as well as in the setter, for var) but allows us to change what we do before returning it to the caller.
In Java, this would look like this:
If we require it, we can also drop the backing field altogether and define it as a standalone getter:
In both cases it still looks like the same property to any component using it, so we can keep our code encapsulated at the same time as being able to keep our code terse. Also, in comparison to Java, we can keep accessor functions close to the property definition, as opposed to having the field at the top of the class and the method after the constructor.
Setters
Similarly to getters, we can define how we want to set a value.
This way we can ensure that the value set to our property always follows the given rules in the setter. In Java we would do this like:
If we have a property that we want to be read-only for callers but we also want to change it internally we can modify the setter:
Moreover, if we don’t want to change the way we assign the property, but we want to restrict access to the setter, we can also add a visibility modifier to it but leave it without a body.
This means that, externally we can see the userName
as a read only property but inside the scope of that property we are able to set it. This saves having to have a private property and a separate public one just referencing the latter.
Changing the visibility modifier to something like internal
or protected
can help with API design where we want to be able to change properties of objects we are sending out to the world, but not to allow other to change them.
Delegation
There is yet another way to declare properties: using delegation. As we know, properties are just functions so, in Koltin, we can delegate these functions to an object:
To do this, UserSource
can extend either ReadOnlyProperty
or ReadWriteProperty
. The first one requires a getValue
function which acts as the getter. On the other hand, ReadWriteProperty
also requires a setValue
to act as a setter.
Extending from one of these interfaces is optional as these operator functions can be defined without inheritance too:
Property delegation is rather powerful as it gives us extra details about the property when it is accessed or assigned. For example, we can get the name of the property, which can be useful when declaring extension properties.
Property delegation in action
As an example of delegation, on Android we can define a type that represents properties inside a Bundle
:
Then we can declare an extension property over a Bundle
:
The name of the property ("sessionId"
in this case) is used any time the property accessed or set on a Bundle
predictably.
However, we need to make sure that we don’t expose them to too much of our code. One way to limit access to them is to make the file or class private.
Another one, among many, would be to create a custom scope:
Where we can jump into when needed:
Which helps avoiding polluting Bundle properties with global extensions.
It may not be evident at first but, even data defined inside a block of code (aka variables in Java) is also a property in Kotlin. I’ve personally tried not to call them variables in the past to make it pertinent that they follow the same patterns as any of the other scopes: member, companion, and global/package.
Member properties
They are part of a type and can be defined either as part of a constructor or anywhere in the type, which complicates things when thinking about where to place them in the class when they are private.
The consensus in Java is to put private fields at the top of the class and private methods underneath the constructors and public methods (and, often after they get used). Constructors are a completely different story so we’ll ignore them for this case. However, it’s still not clear where to put private properties.
Since, in reality, they are functions (and sometimes don’t have a backing field) then where should we put them? We are not going to get into details as it depends too much in each situation.
Companion properties
One of the things that we don’t have in Kotlin is static scopes. However, we can create singleton objects which are close enough. A particular case of those is companion objects.
These can be accessed directly from the type as if it had a static scope as this companion object singleton is shared between all the instances of the class.
Although it may feel like we should use upper snake case (using underscores), there is no particular reason why these should follow such convention as we are still talking about properties.
Global/package properties
Finally, if we declare a property in a file, it becomes “free” from other components as a first-class citizen, which means that it lives in the packages where the file is defined and not inside of a type. When compiled, we can find it inside of the object singleton that Kotlin creates for each file, but the language hides that fact from us.
If we declare a property const val
then it becomes an actual constant that the compiler can inline. So-called constants in Java are often just static fields. In both cases, only when they are a String or a primitive type they benefit of the compiler inlining. That’s why in Kotlin, constants can only be either a string or a primitive value.
In Kotlin, since these are more strict constants and purely immutable, I think that it seems entirely appropriate to use upper snake case to name them. However, all depends on what your team decides in the end.
Conclusion
We shouldn’t think of properties in Kotlin in the same way as variables, fields or constants. Properties are an incredibly versatile feature from the language. To all purposes, we can see them as functions rather than object references.
Hopefully, this has helped you get a better understanding of how some of the features from Kotlin properties work.
#Spam
If you like this content and want to know when I post the next part or just have a chat, don’t forget to follow me on here (Medium) and on Twitter:
Also, if you want to extend your Kotlin knowledge in the functional side of things come and have a look at our courses at: