CARGO  CULT
PROGRAMMER

Kotlin Basics: Properties

If you just started with Kotlin, there’s a good chance that you are overwhelmed with the new possibilities. This is especially true if you originally came from Java, where concepts like Properties or Function Types simply do not exist.
In this post, I will address some of the questions concerning Properties a beginner might have when starting with Kotlin.

Properties and Fields

Unlike Java, Kotlin has no fields (well, technically Kotlin does have fields, but conceptionally it doesn’t).

Consider the two examples:

public class Banana {
  public String color = "green"
}
class Banana {
  var color = "green"
}

On the first glance, they seem equivalent. But they aren’t.
Kotlin has no fields and color can therefore not be accessed directly but will be hidden behind getColor and setColor methods (so called accessor methods). These methods will be hidden if we write pure Kotlin code, but they are still there for interoperability with java.

Accessor methods are generated for us by the Kotlin compiler. But we can write the getColor and setColor methods ourselves, if we want to.

class Banana {
    var color = "green"
        get() = field
        set(value) {field=value}
}

Writing it like this is equivalent to the previous example, where we left out the get() and set() methods. The field identifier references the backing field of this property. These backing fields are used to store the actual value of a property and are automatically generated if we use the default implementation of get() or set() or if we write a custom accessor and use the field identifier.

Writing the accessors ourselves is pointless unless we want to do intercept the access of the property.
Maybe we only want to allow certain values:

class Banana {
    var color = "green"
        set(value) {
          if(value == "yellow" || value == "green"){
            field = value
          }
        }
}

With this, the property (that is, the backing field) will not change unless the new value is either "yellow" or "green".

Read-only Properties vs. “Normal” Properties

As mentioned above, a backing field will not be generated unless we actually use it (be it directly by referencing field or indirectly by using a default accessor).

Let’s change the example a bit so that it’s not using a backing field:

class Banana {
    var ripeness = 1

    var color: String = "green"
        get() = when {
            ripeness > 80 -> "brown"
            ripeness > 50 -> "yellow"
            else -> "green"
        }
}

I added a new mutable property ripeness. The old color property will now return a different result, depending on the value stored in ripeness. Because color is declared as var, and therefore a default set is generated, it will still generate a backing field. This backing field is really useless though:

val banana = Banana()
banana.color = "blue"

println(banana.color)

The println(banana.color) will never print "blue". It will call the get-function and there fore return "green".

Since we don’t want useless backing fields (it’s a bit obscure and needs memory) we get rid of the setter by declaring our property as val, which means as read-only.

class Banana {
    var ripeness = 1

    val color: String
        get() = when {
            ripeness > 80 -> "brown"
            ripeness > 50 -> "yellow"
            else -> "green"
        }
}

This val property will not offer a set method.

Read-only Properties vs. Functions

Writing a property like this is essentially the same as writing a function.

class Banana {
    var ripeness = 1

    fun getColor(): String = when {
            ripeness > 80 -> "brown"
            ripeness > 50 -> "yellow"
            else -> "green"
    }
}

But I prefer the property syntax. A rule of thumb can be “if it describes the object, it’s a property. If does something with the object or with another object, it’s a function”.

I think the C# explanation on “Choosing Between Properties and Methods” is useful for Kotlin as well.

The official style guide also has a section about this.

A Possible Pitfall

With many new possibilities it easy to run into problems. One problem I had was forgetting the get for a property.

class Banana {
    var ripeness = 1

    val color: String = when {
            ripeness > 80 -> "brown"
            ripeness > 50 -> "yellow"
            else -> "green"
        }
}

While this is syntactically correct, it is not what we actually want. By omitting the get accessor, we assign the value of the backing field. This assignment takes place during the construction of our Banana object. This means that the result will be computed once and then never again. Since the ripeness is initially set to 1, calling banana.color will always return "green".

Learn more

The Properties page is a good starting point, as well as Visibility Modifiers, as they can also be applied to properties.
Some more advanced property topics include Overriding Properties and Delegated Properties

© 2024 Lovis Möller