При использовании делегирования класса, есть ли способ переопределить члены, используемые методами объекта делегата? - PullRequest
0 голосов
/ 03 июня 2018

При использовании делегирования классов в Kotlin вы можете переопределять членов.Однако на справочной странице о делегировании написано:

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

Я хочу переопределить свойство, которое используется методами объекта делегата, чтобы методы объекта делегата вызывали это переопределенное свойство.Как говорится в документации, переопределение свойства с ключевым словом override не позволяет этого сделать.Есть ли способ, которым я могу добиться этого поведения?Если нет, то является ли это признаком того, что вместо этого я должен использовать наследование?

Вот пример кода:

interface Base {
    val message: String
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
    // This property is not accessed from b's implementation of `print`
    override val message = "Message of Derived"
}

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print()
}

Этот код выводит «BaseImpl: x = 10», но я хочуэто, чтобы напечатать "Сообщение Производного".

1 Ответ

0 голосов
/ 29 сентября 2018

Проблема заключается в сгенерированном коде:

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, и, следовательно, печатает собственное сообщение.

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