Прямые ссылки на публичные уроки для быстрого старта и стабильной индексации lesson-страниц.
Когда мы обращаемся к свойству класса, кажется, что делаем это напрямую. Однако Kotlin под капотом генерирует сеттеры и геттеры — методы для получения и изменения свойств. По аналогии с getNumberOfPages() и setNumberOfPages() из прошлого урока, только на уровне языка.
Для демонстрации создадим класс BabelFish (Вавилонская рыбка, переводящая языки через сигналы мозга). Изменяемое поле будет хранить уровень нервного сигнала:
class BabelFish { var nerveSignalLevel: Int = 200 } fun main() { val fish = BabelFish() println("old value: ${fish.nerveSignalLevel}") fish.nerveSignalLevel = 400 println("new value: ${fish.nerveSignalLevel}") }
При чтении свойства вызывается геттер, при записи нового значения — сеттер. При необходимости их можно кастомизировать.
Геттер прописывается под свойством как функция get(). После неё через знак равно указывается возвращаемое значение.
Ключевое слово field обозначает само поле, для которого пишется геттер. Реализация ниже является дефолтной — прописывать её явно не нужно:
var nerveSignalLevel: Int = 200 get() = field
Геттер может возвращать фиксированное произвольное значение — тогда именно оно будет отдаваться при каждом обращении к свойству:
var nerveSignalLevel: Int = 200 get() = 250
Сеттер вызывается при записи нового значения в свойство класса:
var nerveSignalLevel: Int = 200 get() = field set(value: Int) { field = value }
value — параметр функции, в который поступает новое значение. Имя может быть любым, тип совпадает с типом свойства. field — поле класса, которому присваивается это значение. Реализация выше является дефолтной.
Реальная польза сеттера — добавление дополнительной логики при записи значения. Добавим переменную isTranslated, которая должна автоматически становиться true, когда уровень сигнала превышает 300:
class BabelFish { var isTranslated: Boolean = false var nerveSignalLevel: Int = 200 get() = field set(value: Int) { field = value if (value > 300) { isTranslated = true println("isTranslated = true") } } }
fun main() { val fish = BabelFish() println("old value: ${fish.nerveSignalLevel}") println("old value: ${fish.isTranslated}") fish.nerveSignalLevel = 400 println("new value: ${fish.nerveSignalLevel}") println("new value: ${fish.isTranslated}") }
При установке значения 400 сеттер сработает и isTranslated станет true.
Кастомизация геттера полезна, когда нужно вычислять возвращаемое значение. Добавим nullable-коэффициент в конструктор и используем его в геттере:
class BabelFish( private val coefficient: Int? ) { var isTranslated: Boolean = false var nerveSignalLevel: Int = 200 get() = if (coefficient != null) field * coefficient else field set(value: Int) { field = value if (value > 300) { isTranslated = true println("isTranslated = true") } } }
fun main() { val fish = BabelFish(null) println("old value: ${fish.nerveSignalLevel}") val fish2 = BabelFish(2) println("old value: ${fish2.nerveSignalLevel}") val fish3 = BabelFish(21) println("old value: ${fish3.nerveSignalLevel}") }
Геттер вернёт 200, 400 и 4200 соответственно.
Важно: внутри геттера и сеттера всегда используйте
field, а не имя самой переменной. Обращение к переменной напрямую приведёт к рекурсивному вызову и зависанию программы.