3

Assume this simple trait. A trait which exposes a val x. Since the initialization costs of x are very high I choose val x =... over def x =...

trait ScalaTrait {

  self =>

  val x: Int = SomeVeryExpensiveOperation.do()

}

Now how about a Java class implementing this trait. I'm of course obliged to implement val x using a public method in my Java class. But here is the catch: because the public method and my trait's val x share the same name I can't just delegate to the trait implementation:

class JavaClass imlements ScalaTrait {
    @Override
    public x() {
        /* I can't hand over this call to x() of the trait,
           since this would be a recursive call inside the Java class itself*/
        return x(); <-- so, this no can do!
    }

}

My question is what exactly should go inside method x()?

As an alternative I could provide a public def accessor to that private val x field. Like so:

trait ScalaTrait {

  self =>

  private val _x: Int = SomeVeryExpensiveOperation.do()
  def x = _x

}

But still, I'm now forced to implement that private val _x in my Java class, too, regardless of the private access modifier. How can I make this work and why must my Java class implement the private val field in the first place?

1 Answer 1

3

You don't really want to do this. You're going to have to dive into the guts of how Scala implements mixins and reimplement it by hand in Java.

Here is a simple example, all in Scala:

trait X {
  val x = 5
}

class Y extends X

We can use scalac -Xprint:mixin to get an intermediate stage in the Scala compilation process that shows what Scala does to implement Y:

[[syntax trees at end of                     mixin]] // test20.scala
package <empty> {
  abstract trait X extends Object {
    <accessor> def X$_setter_$x_=(x$1: Int): Unit;
    <stable> <accessor> def x(): Int
  };
  class Y extends Object with X {
    <stable> <accessor> def x(): Int = Y.this.x;
    private[this] val x: Int = _;
    <accessor> def X$_setter_$x_=(x$1: Int): Unit = Y.this.x = x$1;
    def <init>(): Y = {
      Y.super.<init>();
      X$class./*X$class*/$init$(Y.this);
      ()
    }
  };
  abstract trait X$class extends  {
    def /*X$class*/$init$($this: X): Unit = {
      $this.X$_setter_$x_=(5);
      ()
    }
  }
}

All that stuff for Y you are going to have to implement by hand in Java. You need to define the field (x), the accessor method (also x), the setter method (X$_setter_$x_), and put the call to X$class.$init$ in your constructor.

Sign up to request clarification or add additional context in comments.

2 Comments

The initialiser is the real problem maker here, exposes a lot that you otherwise don't. And that's something that can't be fixed with the migration to a Java8 runtime.
I guess the safest approach is not to extend Scala traits from Java. Instead I let my Java code use the instantiated Scala class (I'm exposing the trait's features via delegation).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.