Menu
Coddy logo textTech

Пользовательские аксессоры

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 7 из 57.

У каждого свойства Kotlin есть геттер, а у каждого var также есть сеттер. По умолчанию они считывают и записывают сохранённое значение. Пользовательский геттер вместо этого вычисляет значение при каждом чтении:

class Rectangle(var width: Int, var height: Int) {
    val area: Int
        get() = width * height
}

Внутри main:

val r = Rectangle(3, 4)
println(r.area)
r.width = 10
println(r.area)

Вывод:

12
40

Пользовательский сеттер выполняет код при каждом присваивании свойству. Внутри него value — это присваиваемое значение, а field — сохранённое значение, стоящее за свойством, то есть резервное поле:

class Thermostat {
    var target = 20
        set(value) {
            field = value.coerceIn(10, 30)
        }
}

Внутри main:

val t = Thermostat()
t.target = 42
println(t.target)
t.target = 18
println(t.target)

Вывод:

30
18

Внутри аксессора используйте field, а не имя свойства: запись target = value внутри сеттера target вызвала бы этот сеттер снова и снова, что привело бы к бесконечной рекурсии:

var target = 20
    set(value) {
        // target = value        // вызывает этот сеттер снова: бесконечная рекурсия
        field = value             // сохраняет значение
    }

Сеттер также является местом для реакции на изменения, например для их подсчёта или журналирования:

class Player(name: String) {
    var changes = 0
        private set
    var name = name
        set(value) {
            field = value.trim()
            changes++
        }
}

Внутри main:

val p = Player("ada")
p.name = "  Ada  "
p.name = "Ada L."
println("${p.name} after ${p.changes} changes")

Вывод:

Ada L. after 2 changes
challenge icon

Задание

Легко

Завершите класс Circle(radius: Double). radius — это var, сеттер которого сохраняет отрицательные значения как 0. Добавьте доступные только для чтения свойства diameter и area (π r² с использованием Math.PI) с пользовательскими геттерами, а также label, который возвращает r=2.0 d=4.0 a=12.57, округляя площадь до двух знаков после запятой ("%.2f".format(area)).

Предоставленный код считывает по одному радиусу в каждой строке, выводит label, затем удваивает радиус последнего круга и снова выводит label, поэтому значения должны соответствовать радиусу.

Ваш код должен находиться в Circle.kt. В Main.kt содержится предоставленный код ввода/вывода, и его нельзя редактировать.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    val input = generateSequence(::readLine).toList()
    var last = Circle(0.0)
    for (line in input) {
        last = Circle(line.toDouble())
        println(last.label)
    }
    last.radius *= 2
    println(last.label)
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin