Property backing field access suggestion


#1

First off, very promising language! That being said, I am not sure about an aspect of the Properties Features. Properties allow the outside to access the internals in restricted way. In Kotlin, backing fields are implicit which is fine, but what bothers me is the access pattern: Why is internal code using (possibly) publicly exposed accessor methods to reach internal data?

When using the property as an identifier within the class, kotlin will call the getter method to access the data. It seems to me that the correct default here should be that code within the class (and subclasses) should reach the backing field directly (unless no backing field exists). With the current syntax, problems start arising when adding extra functionality into the getter or setter. This extra functionality is meant to target code accessing the object from the outside only, but now it suddently affects internal code as well.

Take this scenario for example:

open class Person(public open var names: MutableList<String>) {

  public fun addName(name: String) {
  names.add(name)
  }

  public fun doWhatever() {
  // This is an internal modification
  names = arrayListOf()
  }
}

class User(names: MutableList<String>) : Person(names) {

  public var externalModificationCount: Int = 0
  private set

  override var names: MutableList<String>
  get() {
           //clone defensively
           return java.util.ArrayList(super.names)
  }
  set(value) {
           // I want to restrict certain external modifications
           if(value.isEmpty()) {
           throw IllegalArgumentException()
           }
           super.names = value
           // I want to increase count for external modifications
           externalModificationCount++
  }
}

fun main(args : Array<String>) {
  val john = Person(arrayListOf(“John”))
  john.addName(“Smith”)
  john.doWhatever()

  val peter = User(arrayListOf(“Peter”))
  peter.addName(“Smith”) // FAIL: does not add the name because it added the name into the copied list
  peter.doWhatever() // FAIL: overridden names checks for empty lists

  // Assume there was no assert, we get to this point
  print(peter.externalModificationCount) // FAIL: prints 2 instead of 1 because of doWhatever changing the names property through the setter

}

I would suggest that using the property identifier as a bare word (‘names’ here) from within the class and subclasses should access the backing field since that is what we mean to do most of the time. An alternative syntax can be available to access the property through the accessors from within the class (something that will be less likely to be used).


#2

WHat you request is supported via $name.


#3

Andrey,

Thanks for replying. I did notice that, which is great because I think it will be quite useful. Btw, the docs need updating:

The $counter field can be read or written inside the accessors of counter and can only be assigned to inside constructors. In other places, there’s no access to $counter.


But my problem is that by making ‘name’ use the property accessors and ‘$name’ access the backing field, from within the class, you are encouraging people to use the first, and thus write code that is brittle. When changing the getter or setter in java, you can assume with a relative peace of mind that you are changing behavior for external code only. If a getter or setter is used internally that means the author explicitely wanted the same behavior as external code to be applied. In the case of Kotlin, everytime you change an accessor you would have to look through the code of your class and the code of your super classes and make sure that your change will not break the code. If you find that that is the case, you will need to rename all references of ‘name’ over to ‘$name’. Then when another person is adding even more code, they will use ‘name’ again as that is the default and possibly hit an issue (least amazment problem). To solve this, I could use $name everywhere, but that’s ugly, and I think the language should be encouraging good practices by default.

It feels to me like there is a lack of orthogonality here and unwanted side effects. IMO, accessors should be reserved (by default) to external code. If someone chooses to call the getter and setter from within the class, they should do it explicitely.


#4

The docs are fixed. Thank you for the report.

I don not agree with your point about encouraging bad practices: access to the data should be uniform throughout the system, direct use of backing fields is an extraordinary measure.