Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't property initializers call a custom setter?

Tags:

kotlin

From the Kotlin documentation, custom setters are allowed:

class Test {
  var stringRepresentation: String
    get() = field
    set(value) {
      setDataFromString(value) 
    }

  init {
    stringRepresentation = "test"
  }

  private fun setDataFromString(value: String) { }
}

But you cannot have a custom setter without a custom getter (and initialize from the init block):

class Test {
  // Compilation error: "Property must be initialized"
  var stringRepresentation: String
    set(value) {
      setDataFromString(value)
    }

  init {
    stringRepresentation = "test"
  }

  private fun setDataFromString(value: String) { }
}

Although you can have a custom getter without a custom setter, no problem here:

class Test {
  var stringRepresentation: String
    get() = field 

  init {
    stringRepresentation = "test"
  }

  private fun setDataFromString(value: String) { }
}

So why can't you use a custom setter with a property initialized from within the init block, and why does the init block invoke the custom setter while the property initializer assigns directly, bypassing the custom setter?

class Test {
  var stringRepresentation: String = "" // Does not call custom setter
    set(value) {
      setDataFromString(value)
    }

  init {
    stringRepresentation = "test" // Calls custom setter
  }

  private fun setDataFromString(value: String) { }
}
like image 575
breandan Avatar asked Dec 19 '16 00:12

breandan


1 Answers

Property initializers doesn't call custom setter because their purpose is to provide the default value.

Unlike in Java, in Kotlin not only local variables must be initialized before their first access but class properties as well.

In Java this is valid.

public class Test {
    public String str;

    public static void main(String[] args) {
        System.out.println(new Test().str);
    }
}

In Kotlin this is not.

class Parent {
    var str: String?
}

fun main(args: Array<String>) {
    Parent().str
}

For this reason custom setter needs its property to be initialized either by property initializer or by the constructor. Take a look at the following example.

class Test {
    var stringRepresentation: String = "a" // Default value. Does not call custom setter
        get() = field
        set(value) {
            println("Setting stringRepresentation property to %s. Current value is %s.".format(value, field))
            field = setDataFromString(value)
        }

    init {
        this.stringRepresentation = "b" // Calls custom setter
    }

    private fun setDataFromString(value: String): String {
        println("Setting stringRepresentation property to %s.".format(value))
        return value
    }
}

fun main(args: Array<String>) {
    Test().stringRepresentation = "c" // Calls custom setter
}

Property stringRepresentation is initialized to "a" opon instantiation of its class without calling setter. Then init block is called and sets value to "b" using setter. Then to "c" using setter.

like image 141
Januson Avatar answered Oct 17 '22 13:10

Januson