Делегирование
Делегирование интерфейсов через by, делегированные свойства и приоритет override.
4 вопросов
JuniorТеорияОчень частоЧто делает делегирование интерфейса через by в Kotlin?
Что делает делегирование интерфейса через by в Kotlin?
class B(p: I) : I by p заставляет B реализовать I, перенаправляя каждый метод I объекту p. Компилятор генерирует перенаправляющие методы, поэтому B переиспользует поведение p без ручных вызовов — композиция вместо наследования.
Типичные ошибки
- ✗Понимать
byкак наследование, а не перенаправление методов хранимому объекту - ✗Путать делегирование интерфейса с делегированием свойств вроде
by lazy - ✗Думать, что делегат
pкопируется, а не используется по ссылке
Уточняющие вопросы
- →Что будет, если делегирующий класс к тому же объявит свой
overrideметода? - →Почему делегирование часто предпочитают наследованию для переиспользования кода?
JuniorТеорияЧастоЧто откладывает by lazy и какие у него режимы потокобезопасности?
Что откладывает by lazy и какие у него режимы потокобезопасности?
by lazy откладывает инициализатор до первого чтения свойства, затем кэширует результат для всех последующих чтений. Режим по умолчанию SYNCHRONIZED берёт блокировку, так что инициализатор выполняет ровно один поток; PUBLICATION может выполнить его в нескольких потоках, но публикует первый результат; NONE снимает синхронизацию.
Типичные ошибки
- ✗Думать, что
by lazyперезапускает инициализатор при каждом чтении вместо кэширования - ✗Считать режимом по умолчанию
NONE, а неSYNCHRONIZED - ✗Путать
by lazyсlateinit, который откладывает присваивание, но ничего не кэширует
Уточняющие вопросы
- →Когда безопасно опускаться до
LazyThreadSafetyMode.NONE? - →Почему
by lazyможет стоять заval, аlateinit— нет?
MiddleТеорияИногдаКак работает делегат свойства и что добавляет stdlib-делегат Delegates.observable?
Как работает делегат свойства и что добавляет stdlib-делегат Delegates.observable?
var x: T by d переписывает каждое чтение в d.getValue(thisRef, property), а запись — в d.setValue(thisRef, property, value), поэтому делегатом может быть любой объект с этими operator-функциями. Delegates.observable хранит значение и после каждой записи зовёт ваш колбэк со свойством, старым значением и новым.
Типичные ошибки
- ✗Думать, что делегат вызывается рефлексией, а не по соглашению об
operator-функциях - ✗Ожидать, что
Delegates.observableсрабатывает до записи и может её отменить - ✗Считать, что делегатом может быть только наследник
ReadWriteProperty
Уточняющие вопросы
- →Какой stdlib-делегат всё же позволяет отклонить запись и как он об этом сообщает?
- →К чему даёт доступ делегату аргумент
KProperty, передаваемый вgetValue?
MiddleТеорияИногдаПри делегировании by берёт ли верх явный override в самом классе?
При делегировании by берёт ли верх явный override в самом классе?
Да. override в делегирующем классе всегда берёт верх над делегатом — by поставляет лишь непереопределённые методы. Если класс делегирует один интерфейс двум объектам с общим методом, конфликт не разрешается и класс обязан явно его переопределить, иначе не скомпилируется.
Типичные ошибки
- ✗Думать, что метод делегата перекрывает явный
overrideв классе - ✗Считать, что два делегата одного интерфейса сольются без ошибки компиляции
- ✗Полагать, что порядок клауз
byв исходнике решает победителя
Уточняющие вопросы
- →Какая ошибка компиляции возникает, когда два делегированных интерфейса делят сигнатуру метода?
- →Видит ли объект-делегат вызовы, сделанные через
overrideсамого делегирующего класса?