Java Scala编译器如何处理具体的trait方法?

Java Scala编译器如何处理具体的trait方法?,java,scala,traits,Java,Scala,Traits,如果我有以下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扩展它,编译器会说Ordered的所有具体方法都丢失了。那么,这是否

如果我有以下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扩展它,编译器会说Ordered的所有具体方法都丢失了。那么,这是否意味着Scala编译器只是将Ordered的具体方法的实现放在具体的Scala类中

这看起来非常浪费,因为我可以有几十个具体的类来实现MyOrdered,它们都会得到同一个代码的副本,而事实上,只需将它直接放在基类MyOrdered中就足够了。此外,这使得创建Java友好的Scala API非常困难。除了通过使用伪方法实现使类具体化之外,还有什么方法可以强制Scala编译器将方法定义放在它应该放在的地方呢

更有趣的是在Scala特性中声明了一个具体的方法final。在这种情况下,它仍然没有在扩展trait的抽象Scala类中实现,但是它不能在扩展抽象Scala类的Java类中实现,因为它被标记为final。这绝对是一个编译器错误。最终的抽象方法毫无意义,即使它们在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, 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

 ...
最后,让我们检验一下您的假设,即
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 **
....
scala>M类扩展了MyOrdered{def id=2}
定义类M
scala>:javap-vm
从“”编译
公共类M扩展MyOrdered实现scala.ScalaObject
....
**除了id之外,没有其他方法**
....
不,看起来这里没有代码重复

最后,

  • Scalac用具体的方法处理traits,所以不要试图在Java中继承它们。抽象类应该可以

  • JVM本身不支持符号方法名、Scala单例对象,也不支持具体方法的特性,因此Scala编译器需要进行一些转换,并使用保留的符号$

如果您在Java互操作方面仍然存在问题,希望
:javap
能够帮助您诊断具体问题。

注意:这(2016年8月)可能会在
编译器/scala/tools/nsc/后端/jvm
中对问题进行一些优化:

特征超级访问器的SD-192变更方案 而不是将trait方法主体的代码放入静态方法中, 将其保留为默认方法。
静态方法(需要作为超级调用的目标)现在使用
invokespecial
精确地调用该方法


编译器错误应该向issue tracker报告,并提供显示问题的最小源代码示例。是的,但我不会轻易报告错误,所以我希望先让一些人同意我的观点。毕竟,我可能弄错了。我很乐意帮助您验证,但我不理解您描述的问题。小型Scala代码示例+描述预期行为将非常有帮助。请随意编辑您的问题。当涉及到扩展一些从Java实现的方法的特性时,最好的答案是:不要这样做。这迟早会成为一个问题。原因是Java没有Scala可以映射到的对应项,因此它使用了各种各样的技巧,从Java的角度来看,这些技巧令人困惑。我认为最终trait方法的问题是Eclipse的“表示编译器”的问题,而不是Scala的问题。无论如何,由于用Java代码扩展特性是“不受支持的”,如何处理“final”本质上是无关紧要的。是的,我花了几个小时,但我得出了相同的结论。我认为我遇到的问题与scalac如何在类文件中指定泛型参数有关。查看反编译的具体scala类,我看到“$greater”。。。以java.lang.Object为例,它们只是从定义它们的抽象基类继承而来。这是我们所期望的。但是javac将拒绝编译一个扩展抽象scala类的Java类,除非它包含“$more”。。。使用MyOrdered作为参数,尽管它们显然既不需要也不使用。奇怪。。。
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 **
....