parent class 中受保护的内联方法无法访问其他受保护的方法

Protected inline method in parent class can't access other protected methods

对于以下示例,我在获取 IllegalAccessError 时遇到问题:

我在名为 arch

的 gradle 模块中声明了一个基础 class
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 模块中声明的 child class 称为 app

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

值得一提的是,我正在使用 Android Studio 创建一个 Kotlin 项目,但是这些 class 都是简单的 Kotlin objects,没有任何 Android 具体参考。当然这两个模块也有不同的封装。

现在,在我的主要入口方法中,我正在执行以下操作(在 app 模块内)

ChildClass().run()

我正在调用我在基 class 中声明的 run() 方法,它正在访问延迟启动的 value 属性,后者又调用 getAnEight()方法。由于所有方法都受到保护,我希望 child class 没有理由不能调用所有这些方法。即使其中一个方法被标记为 inline 并且此调用被方法内容替换,它仍然应该能够很好地调用 getAnEight()

相反,我收到 IllegalAccessErrorBaseClass.getAnEight() is inaccessible to class ChildClass$$special$$inlined$getMeValue。当我删除 inline 修饰符,或者如果我将 BaseClass 放在与 ChildClass 相同的包中时,这个问题就会消失。

这是 Kotlin 编译器中的错误吗?或者如果它按预期工作,有人可以向我解释这种行为吗?提前致谢!

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

When an inline function is public or protected and is not a part of a private or internal declaration, it is considered a module's public API. It can be called in other modules and is inlined at such call sites as well.

This imposes certain risks of binary incompatibility caused by changes in the module that declares an inline function in case the calling module is not re-compiled after the change.

To eliminate the risk of such incompatibility being introduced by a change in non-public API of a module, the public API inline functions are not allowed to use non-public-API declarations, i.e. private and internal declarations and their parts, in their bodies.

An internal declaration can be annotated with @PublishedApi, which allows its use in public API inline functions. When an internal inline function is marked as @PublishedApi, its body is checked too, as if it were public.

编辑:我做了一些字节码研究。问题是受保护的 getMyValue() 函数被内联 到 public 构造函数 中。在反编译的字节码中,ChildClass public 构造函数有如下一行:

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

如您所见,它创建了 class ChildClass$$special$$inlined$getMyValue 的实例。让我们看看它的声明:

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

    final BaseClass this[=11=];

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

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

    public final int invoke() {
        return this.this[=11=].getAnEight(); // Here lies the problem
    }
}

当你创建一个 ChildClass 实例时,它的构造函数只创建一个 ChildClass$$special$$inlined$getMyValue 实例,不会抛出任何错误。但是当你调用运行()时,会调用上面class的invoke()方法。这个方法是 public,它的 class 是 public,构造函数是 public,但是 getAnEight 方法是受保护的。这就是我们如何得到这个错误。