Защищенный встроенный метод в родительском классе не может получить доступ к другим защищенным методам

У меня проблема с получением IllegalAccessError для следующего примера:

У меня есть базовый класс, объявленный в модуле gradle под названием Arch

abstract class BaseClass {
    protected abstract val value: Int

    fun run() {
        Log.d("Printme", "value $value")
    }

    protected inline fun getMyValue(): Lazy<Int> = lazy {
        getAnEight()
    }

    protected fun getAnEight() = 8
}

и дочерний класс объявлен в модуле gradle под названием app

class ChildClass: BaseClass() {
    override val value by getMyValue()
}

Стоит сказать, что я создаю проект Kotlin с использованием Android Studio, но все эти классы являются простыми объектами Kotlin без каких-либо специфических ссылок для Android. Конечно, эти два модуля также имеют разные пакеты.

Теперь из моего основного метода ввода я делаю следующее (внутри модуля app )

ChildClass().run()

Я вызываю мой метод run() объявленный в базовом классе, который обращается к ленивому свойству инициированного value , которое в свою очередь вызывает getAnEight() . Поскольку все методы защищены, я ожидаю, что нет причин, по которым дочерний класс не может вызывать все это. Даже если один из методов помечен как inline и этот вызов заменяется содержимым метода, он все равно может getAnEight() вызывать getAnEight() .

Вместо этого я получаю IllegalAccessError том, что BaseClass.getAnEight() is inaccessible to class ChildClass$$special$$inlined$getMeValue$1 . Эта проблема исчезает, когда я BaseClass inline модификатор или BaseClass в тот же пакет, что и ChildClass .

Это ошибка в компиляторе Kotlin? Или кто-то может объяснить мне это поведение, если оно работает как задумано? Заранее спасибо!

Всего 1 ответ


https://kotlinlang.org/docs/reference/inline-functions.html#public-inline-restrictions

Когда встроенная функция является публичной или защищенной и не является частью частной или внутренней декларации, она считается открытым API модуля. Он может быть вызван в других модулях и также встроен в такие сайты вызовов.

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

Чтобы исключить риск возникновения такой несовместимости при изменении непубличного API модуля, встроенным функциям публичного API не разрешается использовать в своих телах объявления непубличного API, то есть частные и внутренние объявления и их части. ,

Внутренняя декларация может быть аннотирована с помощью @PublishedApi, что позволяет использовать ее в открытых функциях API. Когда внутренняя встроенная функция помечается как @PublishedApi, ее тело также проверяется, как если бы оно было общедоступным.

РЕДАКТИРОВАТЬ: я сделал некоторые исследования байт-кода. Проблема в том, что защищенная функция getMyValue () встроена в открытый конструктор . В декомпилированном байт-коде открытый конструктор ChildClass имеет следующую строку:

Lazy var4 = LazyKt.lazy((Function0)(new ChildClass$$special$$inlined$getMyValue$1(this)));

Как видите, он создает экземпляр класса ChildClass$$special$$inlined$getMyValue$1 . Давайте посмотрим на его объявление:

public final class ChildClass$$special$$inlined$getMyValue$1 extends Lambda implements Function0 {

    final BaseClass this$0;

    public ChildClass$$special$$inlined$getMyValue$1(BaseClass var1) {
        super(0);
        this.this$0 = var1;
    }

    public Object invoke() {
        return this.invoke();
    }

    public final int invoke() {
        return this.this$0.getAnEight(); // Here lies the problem
    }
}

Когда вы создаете экземпляр ChildClass, его конструктор создает только ChildClass$$special$$inlined$getMyValue$1 , который не ChildClass$$special$$inlined$getMyValue$1 никаких ошибок. Но когда вы вызываете run (), invoke() метод invoke() класса выше. Этот метод публичный, его класс публичный, конструктор публичный, но метод getAnEight защищен. Вот как мы получаем эту ошибку.


Есть идеи?

10000