Почему инициализаторы свойств не вызывают пользовательский сеттер?

Из документации Kotlin разрешены настраиваемые устройства:

class Test { var stringRepresentation: String get() = field set(value) { setDataFromString(value) } init { stringRepresentation = "test" } private fun setDataFromString(value: String) { } } 

Но у вас не может быть настраиваемый сеттер без пользовательского getter (и инициализация из блока init ):

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

Хотя у вас может быть пользовательский геттер без настраиваемого сеттера, здесь нет проблем:

 class Test { var stringRepresentation: String get() = field init { stringRepresentation = "test" } private fun setDataFromString(value: String) { } } 

Итак, почему вы не можете использовать настраиваемый сеттер с инициализируемым изнутри блоком init , и почему блок init вызывает настраиваемый сеттер, когда инициализатор свойств назначает напрямую, минуя настраиваемый сеттер?

 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) { } } 

Инициализаторы свойств не вызывают настраиваемый сеттер, потому что их целью является предоставление значения по умолчанию.

В отличие от Java, в Kotlin не только локальные переменные должны быть инициализированы до их первого доступа, но и свойств класса.

В Java это действительно.

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

В Котлине это не так.

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

По этой причине настраиваемому сетевому устройству необходимо, чтобы его свойство инициализировалось либо инициализатором свойства, либо конструктором. Взгляните на следующий пример.

 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 } 

Свойство stringRepresentation инициализируется «a» созданием opon своего класса без вызова setter. Затем вызывается блок init и устанавливает значение «b» с помощью setter. Затем на «c» с помощью setter.