Answer
From Java perspective Trait.scala
is compiled into Trait
interface. Hence implementing Trait
in Java is interpreted as implementing an interface - which makes your error messages obvious. Short answer: you can't take advantage of trait implementations in Java, because this would enable multiple inheritance in Java (!)
How is it implemented in Scala?
Long answer: so how does it work in Scala? Looking at the generated bytecode/classes one can find the following code:
interface Trait {
void bar();
}
abstract class Trait$class {
public static void bar(Trait thiz) {/*trait implementation*/}
}
class Foo implements Trait {
public void bar() {
Trait$class.bar(this); //works because `this` implements Trait
}
}
Trait
is an interface
- abstract
Trait$class
(do not confuse with Trait.class
) class is created transparently, which technically does not implement Trait
interface. However it does have a static bar()
method taking Trait
instance as argument (sort of this
)
Foo
implements Trait
interface
scalac
automatically implements Trait
methods by delegating to Trait$class
. This essentially means calling Trait$class.bar(this)
.
Note that Trait$class
is neither a member of Foo
, nor does Foo
extend it. It simply delegates to it by passing this
.
Mixing in multiple traits
To continue the digression on how Scala works... That being said it is easy to imagine how mixing in multiple traits works underneath:
trait Trait1 {def ping(){}};
trait Trait2 {def pong(){}};
class Foo extends Trait1 with Trait2
translates to:
class Foo implements Trait1, Trait2 {
public void ping() {
Trait1$class.ping(this); //works because `this` implements Trait1
}
public void pong() {
Trait2$class.pong(this); //works because `this` implements Trait2
}
}
Multiple traits overriding same method
Now it's easy to imagine how mixing in multiple traits overriding same method:
trait Trait {def bar(){}};
trait Trait1 extends Trait {override def bar(){}};
trait Trait2 extends Trait {override def bar(){}};
Again Trait1
and Trait2
will become interfaces extending Trait
. Now if Trait2
comes last when defining Foo
:
class Foo extends Trait1 with Trait2
you'll get:
class Foo implements Trait1, Trait2 {
public void bar() {
Trait2$class.bar(this); //works because `this` implements Trait2
}
}
However switching Trait1
and Trait2
(making Trait1
to be last) will result in:
class Foo implements Trait2, Trait1 {
public void bar() {
Trait1$class.bar(this); //works because `this` implements Trait1
}
}
Stackable modifications
Now consider how traits as stackable modifications work. Imagine having a really useful class Foo:
class Foo {
def bar = "Foo"
}
which you want to enrich with some new functionality using traits:
trait Trait1 extends Foo {
abstract override def bar = super.bar + ", Trait1"
}
trait Trait2 extends Foo {
abstract override def bar = super.bar + ", Trait2"
}
Here is the new 'Foo' on steroids:
class FooOnSteroids extends Foo with Trait1 with Trait2
It translates to:
Trait1
interface Trait1 {
String Trait1$$super$bar();
String bar();
}
abstract class Trait1$class {
public static String bar(Trait1 thiz) {
// interface call Trait1$$super$bar() is possible
// since FooOnSteroids implements Trait1 (see below)
return thiz.Trait1$$super$bar() + ", Trait1";
}
}
Trait2
public interface Trait2 {
String Trait2$$super$bar();
String bar();
}
public abstract class Trait2$class {
public static String bar(Trait2 thiz) {
// interface call Trait2$$super$bar() is possible
// since FooOnSteroids implements Trait2 (see below)
return thiz.Trait2$$super$bar() + ", Trait2";
}
}
FooOnSteroids
class FooOnSteroids extends Foo implements Trait1, Trait2 {
public final String Trait1$$super$bar() {
// call superclass 'bar' method version
return Foo.bar();
}
public final String Trait2$$super$bar() {
return Trait1$class.bar(this);
}
public String bar() {
return Trait2$class.bar(this);
}
}
So the whole stack invocations are as follows:
- 'bar' method on FooOnSteroids instance (entry point);
- Trait2$class's 'bar' static method passing this as argument and returning a concatenation of 'Trait2$$super$bar()' method call and string ", Trait2";
- 'Trait2$$super$bar()' on FooOnSteroids instance which calls ...
- Trait1$class's 'bar' static method passing this as argument and returning a concatenation of 'Trait1$$super$bar()' method call and string ", Trait1";
- 'Trait1$$super$bar' on FooOnSteroids instance which calls ...
- original Foo's 'bar' method
And the result is "Foo, Trait1, Trait2".
Conclusion
If you've managed to read everything, an answer to the original question is in the first four lines...