gpt4 book ai didi

java - Scala 编译器如何处理具体的特征方法?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:55:58 25 4
gpt4 key购买 nike

如果我有以下 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。在那种情况下,它仍然没有在扩展特征的抽象 Scala 类中实现,但它不能在扩展抽象 Scala 类的 Java 类中实现,因为它被标记为最终的。这绝对是一个编译器错误。 Final 抽象方法毫无意义,即使它们显然在 JVM 中是合法的。

最佳答案

Scala 2.9.1.RC1

让我向您介绍我们在 REPL 中的 friend :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

...

我们看到 scalac 实际上在 MyOrdered生成方法,对应于特征 Ordered 中的那些具体方法。例如,> 方法被翻译成 $greater 并且基本上只是调用 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, 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
...

最后,让我们检验您的假设,即 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 通过具体的方法对 traits 施展魔法,所以不要试图在 Java 中继承它们。抽象类应该没问题。

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

    <

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

关于java - Scala 编译器如何处理具体的特征方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7057166/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com