Проблема заключается в сгенерированном коде:
public final class BaseImpl implements Base {
@NotNull
private final String message;
private final int x;
@NotNull
public String getMessage() {
return this.message;
}
public void print() {
String var1 = this.getMessage();
System.out.println(var1);
}
public final int getX() {
return this.x;
}
public BaseImpl(int x) {
this.x = x;
this.message = "BaseImpl: x = " + this.x;
}
}
public interface Base {
@NotNull
String getMessage();
void print();
}
public final class Derived implements Base {
@NotNull
private final String message;
// $FF: synthetic field
private final Base $$delegate_0;
@NotNull
public String getMessage() {
return this.message;
}
public Derived(@NotNull Base b) {
Intrinsics.checkParameterIsNotNull(b, "b");
super();
this.$$delegate_0 = b;
this.message = "Message of Derived";
}
public void print() {
this.$$delegate_0.print();
}
}
Мне потребовалось некоторое время, чтобы определить его, но вот суть этого:
Делегирование не означает расширение конкретноготип;это означает переопределение базового (в данном случае) интерфейса и отправку переопределенных событий делегированному классу.Вот две вещи, на которые следует обратить пристальное внимание:
public void print() {
this.$$delegate_0.print();
}
И
public final class Derived implements Base {
Что это значит?Это означает, что Derived
никоим образом не отменяет BaseImpl
.Чтобы показать, что я имею в виду, возьмите этот код:
interface Base {
val message: String
fun print()
}
open class BaseImpl(val x: Int) : Base { // This needs to be `open` to override
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(x: Int) : BaseImpl(x) {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main(args: Array<String>) {
val derived = Derived(10)
derived.print()
}
он напечатает Message of Derived
.Зачем?Потому что теперь вы на самом деле переопределяете BaseImpl
.Декомпилируйте код и запомните две предыдущие вещи, на которые я говорил вам обратить особое внимание:
public final class Derived extends BaseImpl {
@NotNull
private final String message = "Message of Derived";
@NotNull
public String getMessage() {
return this.message;
}
public Derived(int x) {
super(x);
}
}
Вы, вероятно, видите, что метод печати пропал;это потому, что вы не отменяете это.Также теперь это extends BaseImpl
вместо implements Base
.Любые вызовы метода getMessage
(который, как вы видите, вызывается в методе print) будут перенаправлены на переопределенный метод, а не на базовый класс.
Если вы это сделалив Java, и вместо того, чтобы использовать получатель, вызывающий поле напрямую, он не будет работать, потому что вы не можете переопределить переменные в Java .Причина, по которой он работает в Kotlin «из коробки», заключается в том, что, как вы видите в декомпилированном коде, он использует методы.
Как вы хотите исправить это зависит от вас.Если вы все еще хотите использовать делегированные свойства, вам нужно переопределить метод print в классе Derived
.Потому что, как я упоминал ранее, между Derived
и BaseImpl
нет никакой реальной связи, за исключением наличия общего родителя.Но Derived
не ребенок BaseImpl
.
Переопределение getMessage()
в качестве EpicPandaForce , следовательно, не будет работать.Кроме того, выполняя override val message = ...
, вы генерируете переопределяющий геттер, как показал мой второй пример
TL; DR: Base by b
реализует Base и отправляет вызовы на print
делегированному BaseImpl
.Derived
не является потомком BaseImpl
, и в результате BaseImpl
не может вызвать getMessage()
getter в Derived
, и, следовательно, печатает собственное сообщение.