Как решить Ограничение реализации: трейт обращается к защищенному методу внутри конкретного метода трейта.

Класс библиотеки Java, который я использую, объявляет

protected getPage(): Page { ... }

Теперь я хочу создать вспомогательный миксин Scala для добавления функций, которые я часто использую. Я не хочу расширять класс, потому что у класса Java есть разные подклассы, которые я хочу расширить в разных местах. Проблема в том, что если я использую getPage() в своем миксине trait, я получаю такую ​​ошибку:

Ограничение реализации: признак MyMixin обращается к защищенному методу getPage внутри конкретного метода признака.

Есть ли решение, как заставить его работать, не затрагивая мои подклассы? И почему существует это ограничение?


Пока что я придумал обходной путь: я переопределяю метод в трейте как

override def getPage(): Page = super.getPage();

Кажется, это работает, но я не полностью удовлетворен. К счастью, мне не нужно переопределять getPage() в моих подклассах, но если бы мне понадобилось, я бы получил два переопределения одного и того же метода, и этот обходной путь не сработает.


person Petr    schedule 09.07.2013    source источник


Ответы (1)


Проблема в том, что даже несмотря на то, что эта черта расширяет класс Java, на самом деле реализация не находится в чем-то, что расширяет класс Java. Рассмотреть возможность

class A { def f = "foo" }
trait T extends A { def g = f + "bar" }
class B extends T { def h = g + "baz" }

В фактическом байт-коде для B мы видим

public java.lang.String g();
  Code:
   0:   aload_0
   1:   invokestatic    #17; //Method T$class.g:(LT;)Ljava/lang/String;
   4:   areturn

это означает, что он просто пересылается на что-то под названием T$class, которое оказывается

public abstract class T$class extends java.lang.Object{
public static java.lang.String g(T);
  Code:
  ...

Таким образом, тело кода вообще не вызывается из подкласса A.

Теперь, со Scala, это не проблема, потому что он просто опускает флаг protected из байт-кода. Но Java требует, чтобы только подклассы могли вызывать защищенные методы.

Итак, у вас есть проблема и сообщение.

Вы не можете легко обойти эту проблему, хотя сообщение об ошибке предлагает, возможно, лучшую альтернативу:

public class JavaProtected {
  protected int getInt() { return 5; }
}

scala> trait T extends JavaProtected { def i = getInt }
<console>:8: error: Implementation restriction: trait T accesses
      protected method getInt inside a concrete trait method.
  Add an accessor in a class extending class JavaProtected as a workaround.

Обратите внимание на последнюю строку.

class WithAccessor extends JavaProtected { protected def myAccessor = getInt }
trait T extends WithAccessor { def i = myAccessor }

работает.

person Rex Kerr    schedule 09.07.2013
comment
Спасибо, это то, чего я хотел избежать, добавляя методы доступа к своим подклассам. Но теперь я понимаю, почему это невозможно. - person Petr; 10.07.2013
comment
@ PetrPudlák - Вам не нужно добавлять аксессоры к каждому подклассу, только к одному подклассу оригинала, разрешающему аксессоры. Тогда все остальные, которые смешиваются с этим признаком, будут расширять класс, обеспечивающий доступ, вместо исходного. (Конечно, защищенные средства доступа будут частью интерфейса, но, по крайней мере, вам нужно будет написать их только один раз.) - person Rex Kerr; 10.07.2013
comment
Проблема в том, что я хочу добавить черту к классам Java, единственный общий суперкласс которых находится в библиотеке Java. Вот почему я хотел использовать свойство mixin, чтобы добавить функциональность без необходимости создавать подкласс Scala для каждого из расширяемых мной классов Java. - person Petr; 10.07.2013
comment
@ PetrPudlák - Ах. Что ж, отражение (в среде без ограничивающего менеджера безопасности) позволит это. Выберите метод, вызовите .setAccessible(true), invoke его, приведите к нужному типу. Я вообще этого не рекомендую. - person Rex Kerr; 10.07.2013