I guess it is not possible to invoke methods implemented in Scala traits from Java, or is there a way?
Suppose I have in Scala:
trait Trait {
def bar = {}
}
and in Java if I use it as
class Foo implements Trait {
}
Java complains that Trait is not abstract and does not override abstract method bar() in Trait
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 (!)
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 interfaceTrait$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
interfacescalac
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
.
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
}
}
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
}
}
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:
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";
}
}
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";
}
}
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:
And the result is "Foo, Trait1, Trait2".
If you've managed to read everything, an answer to the original question is in the first four lines...