Защищенный встроенный метод в родительском классе не может получить доступ к другим защищенным методам - PullRequest
1 голос
/ 15 января 2020

У меня проблема с получением 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 указанные c ссылки. Конечно, эти два модуля также имеют разные пакеты.

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

ChildClass().run()

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

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

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

1 Ответ

1 голос
/ 15 января 2020

https://kotlinlang.org/docs/reference/inline-functions.html#public -inline-ограничения

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

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

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

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

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

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, который не выдает никаких ошибок. Но когда вы вызываете run (), вызывается метод invoke() класса выше. Этот метод - publi c, его класс - publi c, конструктор был publi c, но метод getAnEight защищен. Вот как мы получаем эту ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...