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 methodgetPage
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With