If I have the following Scala class:
abstract class MyOrdered extends Ordered[MyOrdered] {
def id: Int
def compare(that : MyOrdered) : Int =
if (
Scala 2.9.1.RC1
Let me introduce you to our friend :javap
in the REPL, which can be useful for diagnosing errors. First, we define the class,
scala> abstract class MyOrdered extends Ordered[MyOrdered] {
| def id: Int
| def compare(that : MyOrdered) : Int =
| if (that==null) 1 else (id-that.id)
| }
defined class MyOrdered
And then ask to see the JVM bytecode,
scala> :javap -v MyOrdered
Compiled from "<console>"
public abstract class MyOrdered extends java.lang.Object implements scala.math.Ordered,scala.ScalaObject
...
** I'm skipping lots of things here: $less, $lessEq, ... **
...
public boolean $greater(java.lang.Object);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_0
1: aload_1
2: invokestatic #19; //Method scala/math/Ordered$class.$greater:(Lscala/math/Ordered;Ljava/lang/Object;)Z
5: ireturn
LineNumberTable:
line 7: 0
...
public abstract int id();
public int compare(MyOrdered);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_1
1: ifnonnull 8
4: iconst_1
5: goto 17
8: aload_0
9: invokevirtual #38; //Method id:()I
12: aload_1
13: invokevirtual #38; //Method id:()I
16: isub
17: ireturn
LineNumberTable:
line 10: 0
...
We see that scalac actually generates methods in MyOrdered
corresponding to those concrete ones in trait Ordered
. For example, the >
method gets translated to $greater
and basically just calls scala/math/Ordered$class.$greater
. If we like, we can now look up the bytecode for concrete trait definitions,
scala> :javap -v scala.math.Ordered$class
Compiled from "Ordered.scala"
public abstract class scala.math.Ordered$class extends java.lang.Object
...
public static boolean $greater(scala.math.Ordered, java.lang.Object);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_0
1: aload_1
2: invokeinterface #12, 2; //InterfaceMethod scala/math/Ordered.compare:(Ljava/lang/Object;)I
7: iconst_0
8: if_icmple 15
11: iconst_1
12: goto 16
15: iconst_0
16: ireturn
LineNumberTable:
line 46: 0
...
Finally, let's test your hypothesis that a subclass M
of MyOrdered
gets a full copy of all the methods
scala> class M extends MyOrdered { def id = 2 }
defined class M
scala> :javap -v M
Compiled from "<console>"
public class M extends MyOrdered implements scala.ScalaObject
....
** No extra methods besides id **
....
Nope, it looks like there's no code duplication here.
To conclude,
Scalac does some magic with traits with concrete methods, so don't try to inherit from them in Java. Abstract classes should be OK.
The JVM doesn't natively support symbolic method names, Scala singleton objects, nor traits with concrete methods, so the Scala compiler needs to do some translation, and uses the reserved symbol $.
If you're still having problems with Java interop, hopefully :javap
will help you in diagnosing the specific problem.
Note: that latest commit for Scal 2.12.x (August 2016) might optimize things a bit in compiler/scala/tools/nsc/backend/jvm
:
SD-192 Change scheme for trait super accessors
Rather than putting the code of a trait method body into a static method, leave it in the default method.
The static method (needed as the target of the super calls) now usesinvokespecial
to exactly call that method.