How does the Scala compiler handle concrete trait methods?

前端 未结 2 1944
隐瞒了意图╮
隐瞒了意图╮ 2021-02-20 16:07

If I have the following Scala class:

abstract class MyOrdered extends Ordered[MyOrdered] {
    def id: Int
    def compare(that : MyOrdered) : Int =
        if (         


        
2条回答
  •  忘了有多久
    2021-02-20 16:21

    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 ""
    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 ""
    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.

提交回复
热议问题