Mam problem z uzyskaniem IllegalaccessError dla następującego przykładu:

Mam grupę bazową zadeklarowaną w module gradle o nazwie {x0}}

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
}

A klasa dzieci zadeklarowana w module gradle o nazwie {x0}}

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

Warto powiedzieć, że tworzę projekt Kotlinowy za pomocą Android Studio, ale klasy te są wszystkimi prostymi obiektami Kotlin bez żadnych konkretnych referencji Androida. Oczywiście te dwa moduły mają również różne pakiety.

Teraz z mojej metody głównej wpisu robię następujące (wewnątrz modułu {X0}})

ChildClass().run()

Dzwonię do mojej metody run() zadeklarowanej w klasie bazowej, która uzyskuje dostęp do leniwych zainicjowanych value właściwość, która znajduje się w kolei Calling getAnEight() Metoda. Ponieważ wszystkie metody są chronione, spodziewałbym się, że nie ma powodu, aby klasa dzieci nie może nazwać tych wszystkich. Nawet jeśli jedna z metod jest oznaczona jako inline, a to połączenie zostanie zastąpione zawartością metody, powinno być nadal możliwe, aby zadzwonić getAnEight() w porządku.

Zamiast tego odbieram IllegalAccessError mówiąc BaseClass.getAnEight() is inaccessible to class ChildClass$$special$$inlined$getMeValue$1. Ten problem znika, gdy usuwam modyfikator inline, lub jeśli umiełem BaseClass w tym samym pakiecie, co ChildClass.

Czy to błąd w kompilatorze Kotlin? Lub czy ktoś może mi wyjaśnić to zachowanie, jeśli działa zgodnie z przeznaczeniem? Z góry dziękuję!

2
fafaldo 15 styczeń 2020, 16:24

1 odpowiedź

Najlepsza odpowiedź

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

Gdy funkcja inline jest publiczna lub chroniona, a nie jest częścią deklaracji prywatnej lub wewnętrznej, jest uważany za publiczny API modułu. Może być wywoływany w innych modułach i jest również włączony w takich stronach połączeń.

Nakłada to pewne ryzyko niekompatybilności binarnej spowodowanej zmianami w module, który deklaruje funkcję inline w przypadku, gdy moduł wywoławczy nie jest ponownie skompilowany po zmianie.

Aby wyeliminować ryzyko takiej niezgodności wprowadzanej przez zmianę niepublicznych interfejsów API modułu, funkcje inline API publiczne nie mogą stosować deklaracji niepublicznych, tj. Deklaracji prywatnych i wewnętrznych oraz ich części, w ich organach .

Deklaracja wewnętrzna może być opatrzona za pomocą @Publishedapi, która umożliwia jego użycie w publicznych funkcjach inline API. Gdy wewnętrzna funkcja inline jest oznaczona jako @Publishedapi, jego ciało jest również sprawdzane, jakby była publiczna.

Edytuj: Zrobiłem kilka badań BYTECODE. Problem polega na tym, że funkcja chronionego GetMyValue () jest wciśnięta do konstruktora publicznego . W dekompilowanym BYTECODE, Publiczny konstruktor dla dziecka ma następną linię:

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

Jak widać, tworzy instancję klasy ChildClass$$special$$inlined$getMyValue$1. Spójrzmy na jego deklarację:

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
    }
}

Podczas tworzenia instancji wklurze dziecięcej konstruktor tworzy tylko instancję ChildClass$$special$$inlined$getMyValue$1, która nie rzuca żadnych błędów. Ale kiedy wywołujesz Run (), {x1}} Metoda powyżej klasy powyżej. Ta metoda jest publiczna, jego klasa jest publiczna, konstruktor był publiczny, ale metoda GetAnight jest chroniona. W jaki sposób otrzymujemy ten błąd.

1
ardenit 15 styczeń 2020, 15:36