Scala编译器如何处理具体的特征方法? [英] How does the Scala compiler handle concrete trait methods?

查看:158
本文介绍了Scala编译器如何处理具体的特征方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有以下Scala类:

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)
}

然后我只需要在Scala中定义id方法来获得具体的类。但是如果我尝试在 Java 中扩展它,编译器会说Ordered的所有具体方法都缺失了。那么,这是否意味着Scala编译器只在具体的Scala类中实现了Ordered的具体方法?

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?

这看起来很浪费,因为我可能有几十个实现MyOrdered的具体类,他们都会得到相同代码的副本,实际上只需将它直接放在基类MyOrdered中即可。此外,这使得创建Java友好的Scala API变得非常困难。有没有办法强制Scala编译器将方法定义放在应该这样做的地方,除了通过使用虚方法实现使类具体化?

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?

甚至更有趣的是在Scala特征中声明一个具体的方法final。在这种情况下,仍然没有在扩展特征的抽象Scala类中实现,但它不能在扩展抽象Scala类的Java类中实现,因为它被标记为final。这绝对是一个编译器错误。最终的抽象方法毫无意义,即使它们在JVM中是合法的,显然也是如此。

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.

推荐答案

Scala 2.9.1 .RC1

让我向你介绍我们的朋友:javap 在REPL中,可以是用于诊断错误。首先,我们定义类,

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

然后要求查看JVM字节码,

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

 ...

我们看到scalac实际 MyOrdered中生成方法 对应于特质 Ordered 中的具体特征。例如,> 方法被转换为 $ greater ,基本上只是调用 scala /数学/有序$类$更大。如果我们愿意,我们现在可以查找具体特征定义的字节码,

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

最后,让我们测试一下 M 的子类 MyOrdered 获取所有方法的完整副本

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.

总结一下,


  • Scalac使用具体方法对特征做了一些魔术,所以不要试图用Java继承它们。抽象类应该没问题。

  • Scalac does some magic with traits with concrete methods, so don't try to inherit from them in Java. Abstract classes should be OK.

JVM本身不支持符号方法名称,Scala单例对象,也不支持具体方法的特征,因此Scala编译器需要做一些翻译,并使用保留符号$。

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

如果你仍然遇到Java互操作问题,希望:javap 将帮助您诊断特定问题。

If you're still having problems with Java interop, hopefully :javap will help you in diagnosing the specific problem.

这篇关于Scala编译器如何处理具体的特征方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆