微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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继承它们.抽象类应该可以.
> JVM本身并不支持符号方法名称,Scala单例对象,也不具有具体方法的特征,因此Scala编译器需要进行一些翻译,并使用保留符号$.

如果您仍然遇到Java互操作问题,希望可以:javap将帮助您诊断具体问题.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐