Можно ли в extension функции Kotlin обратиться к приватным полям расширяемого класса и почему
Что проверяют
Понимаете ли вы, во что extension-функция превращается после компиляции. Правильное «нет» без механизма ценится мало. Следом спрашивают, можно ли переопределить extension в наследнике и что будет, если у класса уже есть член с тем же именем.
Как отвечать
Нет, не может. Extension ничего не добавляет в класс: компилятор делает из него статический метод, где получатель передаётся первым параметром. fun User.showName() из файла UserExt.kt в байткоде выглядит как UserExtKt.showName(User), и из Java его так и вызывают. Это обычный внешний код, и он видит только то, что видно из места объявления:
public: доступно всегда;internal: доступно, если extension лежит в том же модуле;protectedиprivate: недоступны, даже если extension объявлен в том же файле, что и класс.
Исключение одно: extension, объявленный внутри тела класса (member extension). Он член класса и видит его приватные поля, но вызвать его можно только там, где экземпляр этого класса доступен как получатель: внутри класса или в with(obj) { }.
Из статической природы следуют ещё два правила. Функция выбирается по объявленному типу переменной, а не по реальному объекту, так что полиморфизма у extensions нет. А если у класса есть член с той же сигнатурой, всегда побеждает член.
В Android это видно постоянно: core-ktx и другие KTX-библиотеки это набор extensions над публичным API фреймворка, например View.isVisible или SharedPreferences.edit { }.
Где спотыкаются
Думают, что extension «встраивается» в класс, как partial-класс в C#, и поэтому видит всё.
Путают уровни private: на верхнем уровне файла он виден во всём файле, а у члена класса только внутри класса. Отсюда ложная уверенность, что extension в том же файле увидит поле.
Предлагают обойти запрет рефлексией с isAccessible = true. Это работает, но ломается после обфускации R8 и говорит о том, что полю нужен internal или публичный метод.