scalacompiler-errorsprotectedscala-java-interopmultiple-constructors

Subclassing and overriding Java class with protected field access


The scala project is depending on the Java lib that I cannot modify. I need to extend one of the Java classes and override method, however I have issues with protected visibility:

package com.a;

class Foo {
   protected Baz protectedBaz;

   Foo(Int i) { ... }
   Foo(String s) { ... }

   protected void init() { ... }
}

package com.b;

class BarFoo extends Foo {

   BarFoo(Int i) { ... }
   BarFoo(String s) { ... }
}

Now, In scala I need to extend BarFoo, override the init method and modify the protectedBaz:

override def init(): Unit = {
   super.init() // I need BarFoo to do its work
   this.protectedBaz.setA(..)
}

End of problem statement


Attempt

Before I even get there, I need to deal with the constructor problem. I followed the solution here:

object CustomBarFoo {
   def apply(i: Int) = new BarFoo(i) with CustomBarFoo
   def apply(s: String) = new BarFoo(s) with CustomBarFoo
}

trait CustomBarFoo extends BarFoo {
   override def init(): Unit = {
       super.init()
       this.protectedBaz.setA(..) <<< Implementation restriction: trait CustomBarFoo accesses protected variable protectedBaz inside a concrete trait method.
   }
}

The suggested solution here is to subclass and redefine visibility.

I have a classical chicken and egg problem. I cannot create a subclass class CustomBarFoo extends BarFoo because of the constructor issues. But the only way to redefine the visibility is to subclass.


Solution

  • In Scala 2.13.16 the full error message is

    Implementation restriction: trait CustomBarFoo accesses protected variable protectedBaz inside a concrete trait method.
    Add an accessor in a class extending class Foo as a workaround.  <-- !!!
    

    Even if you can't modify Java library, you still can define your own Java class providing a public accessor to the protected field:

    public class SubBarFoo extends BarFoo {
        public SubBarFoo(int i) {
            super(i);
        }
    
        public SubBarFoo(String s) {
            super(s);
        }
    
        public Baz getProtectedBaz() {
            return protectedBaz;
        }
    }
    

    And now you should be able to use this accessor in Scala:

    object CustomBarFoo {
      def apply(i: Int) = new SubBarFoo(i) with CustomBarFoo
      def apply(s: String) = new SubBarFoo(s) with CustomBarFoo
    }
    
    trait CustomBarFoo extends SubBarFoo {
      override def init(): Unit = {
        super.init()
        this.getProtectedBaz.setA()
      }
    }
    

    One more option is to define CustomBarFoo in your Scala code in the same package as Foo in the Java library. Scala protected members are accessible in inheritors but Java protected members are accessible also in the same package.