I have a hard time understanding the purpose of a "backing property"

Viewed 451

I am learning Kotlin right now. For context, I am a Java developer for +10 years.

I stumbled upon the concept of backing properties. As I understand it, the problem to be solved is this: I have a property in a class. I want this property to be mutable and visible only in the containing class, so I declare it as private var. The property should only be modified in the containing class, but it should be readable outside the class. So, the Kotlin docs propose something like this:

private var _word = "test"
val word: String
   get() = _word

This works and fulfills the above requirements, but this looks a bit odd, for a Java developer. In Java, we only need 1 field (there is no such thing as a property in Java):

private String word = "test";

public String getWord() {
   return word;
}

AFAIK, same result.

Today I learned that in Kotlin, I simply can make a setter private. So what about this Kotlin code:

var word = "test"
     private set

All requirements fulfilled, much more concise code.

Im I missing something? Why do I need a backing property?

3 Answers

(This answer starts with some general info about Kotlin properties, as there seems to be general confusion about them.)

The main conceptual difference between Kotlin and Java is that in Java, the fundamental thing is the field, while in Kotlin the fundamental thing is the accessor method(s).

So in Java, you define a field. You can then define explicit getter and/or setter methods for it, if needed. The language doesn't have a concept of ‘property’, nor does it define any explicit connection between the accessors and the field; but there's a long-standing convention in the accessor method names (deriving originally from JavaBeans) that almost everyone follows.

This is clear, but long-winded. And because it's only a convention, the language doesn't force you to match fields with accessors: you can have accessors with different types, setters without getters and vice versa, or even accessors with no field. (Some of those things are desirable, but others are more likely to cause bugs and confusion!)

Kotlin, however, takes the view that the accessors are the important thing. (This is an example of the general principle that you should program to the interface, not caring about how it may be implemented.) In effect, a property is its accessor(s). As long as you have a getter method, then you can get the property's value; and as long as you have a setter method, then you can set it too.

Because getters and setters are usually trivial, Kotlin defines them implicitly unless you tell it otherwise. So when you write:

val myProperty = 1

then Kotlin creates:

  • A private field called myProperty, with the type inferred as Int, and initialised to the value 1.
  • A getter method called getMyProperty(), with public scope (as that's the default), returning an Int.

That's just what you'd do in Java, only more concise.

What's different is that whenever you refer to the property, you're calling the accessor method, not accessing the field directly. (That's why it's called a backing field — its only purpose is for the accessor(s) to use.)

Of course, the effect is identical unless you override the accessor. For example:

val myProperty = 1
    get() {
        LOG.info("Getting value of myProperty = $field")
        return field
    }

(field is a special keyword within getters and setters, referring to their backing field.)

As before, this generates a field and a getter method; but the getter method is specified in the code and not autogenerated.

(I haven't referred to mutable properties or setters, but the principle there is the same: if the property is a var, then you get an autogenerated or explicit setter method too. One Java possibility that Kotlin doesn't support is write-only properties, where some scope can see a setter but no getter. But those are rarely used; you can always use a normal method to set them, which is probably clearer.)

There's one more case to consider. In Java, a getter method can exist on its own, without a corresponding field. (It must then get its value from some other source — maybe by performing a calculation on other fields, or getting it from another object, or even using a hard-coded value.)

You can do the same in Kotlin, by overriding the getter (and setter, if mutable) and not referring to the field, e.g.:

val myProperty: Int
    get() = someOtherObject.someOtherProperty

In this case, there would be no point in generating a field, because it would never be used! So Kotlin recognises this and generates only the accessor method(s).

It's still a property, because it still has accessor method(s), and the rest of the code can't see any difference. Whether it has a backing field or not is purely an implementation detail.


This question is asking about a ‘backing property’. The official docs use that term for a separate, private property which ‘shadows’ an existing one, as shown in this question. (Thanks to cactustictacs for the reference.)

There are specific situations where a backing property can be useful. For example, suppose you need to refer to a specific type within the class, but expose only a supertype publicly; the private property could be of the specific type, and then the public one could be of the supertype.

However, that's not needed simply to restrict the visibility of a setter method: as the question demonstrates, that can be done explicitly (e.g. by adding private set to the property definition).

(In my experience, backing properties are needed very rarely.)

You're right, there's no reason to do this:

private var _word = "test"
val word: String
   get() = _word

Because you can just do this:

var word = "test"
    private set

Just like in Java with a private field and a getter method, you can choose later to change getter logic without breaking external code. In Java you could choose to do something more complicated than use a single private backing field. Maybe calculate something involving multiple backing fields, etc. In Kotlin, you can come back later and change it to use other backing properties later if it gets more complicated, and change only the getter implementation.

The most common reason I've seen to use a backing property is so you can return a less specific type in your public property:

private val _myList: MutableList<String> = mutableListOf()
val myList: List<String> get() = _myList

This code helps prevent outside classes from mutating the returned list.

The Kotlin designers have mentioned in presentations that they plan to add a new syntax in the future that will eliminate the need for a backing property in this use case. Something like this:

// Not valid syntax yet in Kotlin 1.6
private val myList: MutableList<String> = mutableListOf()
    public get: List<String>

Another reason to have a backing property is simply when there isn't a one-to-one correspondence between what you're returning and how it is stored or generated. For example:

private val myRandom = Random(1234)
val aNumber: Int get() = myRandom.nextInt(100)

One other use for backing properties is to allow you to store a null value internally, but expose a non-null type, and provide some fallback value (which you might not want to store, if null has a specific meaning).

Here's the example from the docs:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // Type parameters are inferred
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

What's happening here is your typical singleton getInstance()-style accessor, where if the object isn't instantiated yet, you create and store it before returning it. Unlike a lazy delegate, it's internally a var meaning it can be replaced with a new Map, or null in this example (with some handler code anticipating a potential concurrency issue)

The caller sees none of this though - outwardly it's just a plain val that returns a non-null Map, accessed like any other property, with no need to handle a potential null. All that nullability is abstracted away, it's an internal detail.

You'd have to do the same thing in Java (let's say if you wanted to use the @NonNull annotation on your getter), creating an internal variable and wrangling its state. It's just that in Kotlin, you get a basic backing field "for free" (if you even need one) and it's only if that isn't adequately needs-suiting that you need to start creating and poking at properties yourself

Related