kotlin

Moving forward with Kotlin - Part 1: Properties

Originally published here:
https://medium.com/@pablisco/moving-forward-part-1-properties-d8f695b3c812

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:

val userName = "Alice"

We are creating a property with a backing field behind it. To all effects, we can consider it something like this written in Java:

private String userName = "Alice"
public String getUserName() {
  return userName;
}

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:

val prefix = "user"
val userName = "Alice"
  get() = "$prefix-$field"

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:

private String userName = "Alice"
public String getUserName() {
  return prefix + "-" + userName;
}

If we require it, we can also drop the backing field altogether and define it as a standalone getter:

val userName
  get() = userSource.fetchUser()

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.

var userName = "user-Alice"
  set(value) { field = "user-$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:

private String userName = "user-Alice"
public String getUserName() {
  return userName;
}
public void setUserName(String userName) {
  this.userName = "user-" + userName;
}

If we have a property that we want to be readable but we also want to change it internally we can modify the setter:

var userName = "Alice"
  private set(value) { field = "user-$value" }

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.

var userName = "Alice"
  private set

Meaning that externally we can see the userName as a read-only property but if we have the same scope of that setter, we can set it, which saves having to have 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 others 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:

val userName by UserSource

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.

class UserSource(...) : ReadOnlyProperty<Any, User> {
  override operator fun getValue(
    thisRef: Any, 
    property: KProperty<*>
  ): User = TODO("Not implemented.")
}

Extending from one of these interfaces is optional as these operator functions can be defined without inheritance too:

class UserSource(...) {
  operator fun getValue(
    thisRef: Any, 
    property: KProperty<*>
  ): User = TODO("Not implemented.")
}

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:

object StringBundleProperty {
    operator fun getValue(
        thisref: Bundle, 
        property: KProperty<*>
    ): String? = thisref.getString(property.name)
    operator fun setValue(
        thisref: Bundle, 
        property: KProperty<*>, 
        value: String?
    ) = thisref.putString(property.name, value)
}

Then we can declare an extension property over a Bundle:

val Bundle.sessionId by StringBundleProperty

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:

object UserDataScope {
    val Bundle.sessionId by StringBundleProperty
}

Where we can jump into when needed:

with(UserDataScope) {
    val sessionId = bundle.sessionId
}

Helping us keep the Bundle's properties clear from 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.

class UserSource(...) {
  companion object {
    val defaultUser = User()
  }
}

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 to 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 have a chat, don't forget to follow me on here (Medium) and on Twitter:
https://twitter.com/pablisc0

Also, if you want to extend your Kotlin knowledge in the functional side of things come and have a look at our courses at:
https://twitter.com/functionalhub

Enjoyed this article? There's more...

We send out a small, valuable newsletter with the best stories, app design & development resources every month.

No spam, no giving your data away, unsubscribe anytime.

About Novoda

We plan, design, and develop the world’s most desirable software products. Our team’s expertise helps brands like Sony, Motorola, Tesco, Channel4, BBC, and News Corp build fully customized Android devices or simply make their mobile experiences the best on the market. Since 2008, our full in-house teams work from London, Liverpool, Berlin, Barcelona, and NYC.

Let’s get in contact