If I have the following Scala class:
abstract class MyOrdered extends Ordered[MyOrdered] {
def id: Int
def compare(that : MyOrdered) : Int =
if (that==null) 1 else (id-that.id)
}
Then I only need to define the id method in Scala to get a concrete class. But if I try to extend it in Java, the compiler says that all the concrete methods of Ordered are missing. So, does that mean that the Scala compiler is only putting the implementation of the concrete methods of Ordered in concrete Scala classes?
This seems very wasteful, because I could have dozens of concrete classes implementing MyOrdered, and they would all get a copy of the same code, when in fact it would be enough to just put it directly in the base class MyOrdered. Also, this makes it very difficult to create a Java-friendly Scala API. Is there any way to force the Scala compiler to put the method definitions where it should have done so anyway, apart from making the class concrete by using dummy method implementations?
Even funnier is declaring a concrete method final in a Scala trait. In that case, it is still not implemented in an abstract Scala class that extends the trait, but it cannot be implemented in a Java class that extends the abstract Scala class because it is flagged as final. This is definitely a compiler bug. Final abstract methods make no sense, even if they are legal in the JVM, apparently.
In scala, trait is a collection of abstract and non-abstract methods. You can create trait that can have all abstract methods or some abstract and some non-abstract methods. A variable that is declared either by using val or var keyword in a trait get internally implemented in the class that implements the trait.
Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters. A trait definition looks just like a class definition except that it uses the keyword trait.
Unlike a class, Scala traits cannot be instantiated and have no arguments or parameters.
Traits in Scala have a lot of similarities with interfaces in Java, but a trait is more powerful than an interface because it allows developers to implement members within it. The trait is a combination of abstract and non-abstract methods. Trait can not be instantiated, thus it has no parameters.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With