Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to solve "Implementation restriction: trait ... accesses protected method ... inside a concrete trait method."

A Java library class I'm using declares

protected getPage(): Page { ... }

Now I want to make a helper Scala mixin to add features that I often use. I don't want to extend the class, because the Java class has different subclasses I want to extend at different places. The problem is that if I use getPage() in my mixin trait, I get this error:

Implementation restriction: trait MyMixin accesses protected method getPage inside a concrete trait method.

Is there a solution how to make it work, without affecting my subclasses? And why is there this restriction?


So far, I came up with a work-around: I override the method in the trait as

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

This seems to work, but I'm not completely satisfied. Luckily I don't need to override getPage() in my subclasses, but if I needed, I'd get two overrides of the same method and this work-around won't work.

like image 777
Petr Avatar asked Jul 09 '13 19:07

Petr


1 Answers

The problem is that even though the trait extends the Java class, the implementation is not actually in something that extends the Java class. Consider

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

In the actual bytecode for B we see

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

which means it just forwards to something called T$class, which it turns out is

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

So the body of the code isn't called from a subclass of A at all.

Now, with Scala that's no problem because it just omits the protected flag from bytecode. But Java enforces that only subclasses can call protected methods.

And thus you have the problem, and the message.

You cannot easily get around this problem, though the error message suggests what is perhaps the best alternative:

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.

Note the last line.

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

works.

like image 147
Rex Kerr Avatar answered Sep 28 '22 06:09

Rex Kerr