Scala 是如何在幕后实现加法运算符的?
How does Scala implement the addition operator behind the scenes?
在Scala中,+运算符实际上是对象实现的一个名为+的方法。在 Int 的情况下,来自 Int.scala:
/** Returns the sum of this value and `x`. */
def +(x: Int): Int
并且可以使用 infix notation:
调用没有副作用的 1 元方法
// this
caller method callee
// and this
caller.method(callee)
// are identical, so
1 + 2
// is actually
(1).+(2)
但我在 Int.scala 上找不到该语言实际上如何在 + 方法中执行整数加法。
怎么做到的?
编译器魔法。编译器在 JVM 上转换为固有的 "iadd" 指令。
class Test {
def first(x: Int, y: Int) = x + y
def second(x: Int, y: Int) = (x).+(y)
}
在任何一种情况下都可以准确地编译成您希望的结果
$ javap -c Test.class
Compiled from "Test.scala"
public class Test {
public int first(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
public int second(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
public Test();
Code:
0: aload_0
1: invokespecial #20 // Method java/lang/Object."<init>":()V
4: return
}
其他 JVM 原始操作也会发生类似的事情
如果您自己实现“+”class,它只是被分派到一个普通的方法调用中
class Test2 {
def +(t2: Test2) = "whatever"
def test = this + this
}
变成
$ javap -c Test2.class
Compiled from "Test2.scala"
public class Test2 {
public java.lang.String $plus(Test2);
Code:
0: ldc #12 // String whatever
2: areturn
public java.lang.String test();
Code:
0: aload_0
1: aload_0
2: invokevirtual #19 // Method $plus:(LTest2;)Ljava/lang/String;
5: areturn
public Test2();
Code:
0: aload_0
1: invokespecial #23 // Method java/lang/Object."<init>":()V
4: return
}
请注意该方法名为“$plus”。这是因为就 JVM 而言,“+”不是有效的方法名称。其他不是有效 JVM 名称的符号具有类似的翻译。
在所有这些情况下,scalac 使用静态类型来确定是发出方法调用还是 JVM 原语。
实际的决定是在 https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala 中做出的,这是一个在编译器链中很晚发生的阶段。大多数情况下,无论 x 的类型如何,所有先前阶段都将 x + y 视为方法调用。
在Scala中,+运算符实际上是对象实现的一个名为+的方法。在 Int 的情况下,来自 Int.scala:
/** Returns the sum of this value and `x`. */
def +(x: Int): Int
并且可以使用 infix notation:
调用没有副作用的 1 元方法// this
caller method callee
// and this
caller.method(callee)
// are identical, so
1 + 2
// is actually
(1).+(2)
但我在 Int.scala 上找不到该语言实际上如何在 + 方法中执行整数加法。
怎么做到的?
编译器魔法。编译器在 JVM 上转换为固有的 "iadd" 指令。
class Test {
def first(x: Int, y: Int) = x + y
def second(x: Int, y: Int) = (x).+(y)
}
在任何一种情况下都可以准确地编译成您希望的结果
$ javap -c Test.class
Compiled from "Test.scala"
public class Test {
public int first(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
public int second(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
public Test();
Code:
0: aload_0
1: invokespecial #20 // Method java/lang/Object."<init>":()V
4: return
}
其他 JVM 原始操作也会发生类似的事情
如果您自己实现“+”class,它只是被分派到一个普通的方法调用中
class Test2 {
def +(t2: Test2) = "whatever"
def test = this + this
}
变成
$ javap -c Test2.class
Compiled from "Test2.scala"
public class Test2 {
public java.lang.String $plus(Test2);
Code:
0: ldc #12 // String whatever
2: areturn
public java.lang.String test();
Code:
0: aload_0
1: aload_0
2: invokevirtual #19 // Method $plus:(LTest2;)Ljava/lang/String;
5: areturn
public Test2();
Code:
0: aload_0
1: invokespecial #23 // Method java/lang/Object."<init>":()V
4: return
}
请注意该方法名为“$plus”。这是因为就 JVM 而言,“+”不是有效的方法名称。其他不是有效 JVM 名称的符号具有类似的翻译。
在所有这些情况下,scalac 使用静态类型来确定是发出方法调用还是 JVM 原语。
实际的决定是在 https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala 中做出的,这是一个在编译器链中很晚发生的阶段。大多数情况下,无论 x 的类型如何,所有先前阶段都将 x + y 视为方法调用。