java – Scala编译器如何处理具体的trait方法?
如果我有以下
Scala类:
abstract class MyOrdered extends Ordered[MyOrdered] { def id: Int def compare(that : MyOrdered) : Int = if (that==null) 1 else (id-that.id) } 那么我只需要在Scala中定义id方法来获得一个具体的类.但是,如果我尝试用Java扩展它,那么编译器会说缺少所有具体的Orderered方法.那么这是否意味着Scala编译器只是在具体的Scala类中执行具体的具体方法? 这似乎是非常浪费的,因为我可以有几十个实现MyOrdered的具体类,并且它们都将获得相同代码的副本,实际上只需将它直接放在基类MyOrdered中即可.此外,这使得很难创建一个Java友好的Scala API.有没有办法强制Scala编译器将方法定义放在应该这样做的地方,除了使用虚拟方法实现使类具体化? 即使有趣的是在Scala特征中声明具体的方法最终.在这种情况下,它仍然不能在扩展trait的抽象Scala类中实现,但它不能在扩展抽象Scala类的Java类中实现,因为它被标记为final.这绝对是一个编译器bug.显然,最终的抽象方法没有任何意义,即使它们在JVM中是合法的. 解决方法
Scala 2.9.1.RC1
让我向您介绍一下我们的朋友:REPL中的javap,它可以用于诊断错误.首先,我们定义类, 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 然后请求查看JVM字节码, 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,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 ... 我们看到,scalac实际上是在MyOrdered中生成的,与trait Ordered中具体的方法相对应.例如,>方法被转换为$更大,基本上只调用scala / math / Ordered $class.$greater.如果我们喜欢,我们现在可以查看具体特征定义的字节码, 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,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 ... 最后,我们来测试一下MyOrdered的子类M获得所有方法的完整副本的假设 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 ** .... 不,看来这里没有代码重复. 总而言之, > Scalac使用具体的方法做一些具有特征的魔法,所以不要试图用Java继承它们.抽象类应该可以. 如果您仍然遇到Java互操作问题,希望可以:javap将帮助您诊断具体问题. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |