【发布时间】:2017-03-08 04:25:29
【问题描述】:
我的other question 因重复而被关闭,所以我会再试一次。我也读过this question,我问的是不同的。我有兴趣了解Call-by-Name: => Type 与() => Type 的不同之处的内部实现。
我的困惑来自于查看 javap 和 cfr 反汇编,这两种情况没有区别。
例如ParamTest.scala:
object ParamTest {
def bar(x: Int, y: => Int) : Int = if (x > 0) y else 10
def baz(x: Int, f: () => Int) : Int = if (x > 0) f() else 20
}
javap 输出 javap ParamTest.scala:
public final class ParamTest {
public static int baz(int, scala.Function0<java.lang.Object>);
public static int bar(int, scala.Function0<java.lang.Object>);
}
CFR 反编译输出 java -jar cfr_0_118.jar ParamTest$.class:
import scala.Function0;
public final class ParamTest$ {
public static final ParamTest$ MODULE$;
public static {
new ParamTest$();
}
public int bar(int x, Function0<Object> y) {
return x > 0 ? y.apply$mcI$sp() : 10;
}
public int baz(int x, Function0<Object> f) {
return x > 0 ? f.apply$mcI$sp() : 20;
}
private ParamTest$() {
MODULE$ = this;
}
}
编辑 1:
Scala 语法树:scalac -Xprint:parse ParamTest.scala
package <empty> {
object ParamTest extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def bar(x: Int, y: _root_.scala.<byname>[Int]): Int = if (x.$greater(0))
y
else
10;
def baz(x: Int, f: _root_.scala.Function0[Int]): Int = if (x.$greater(0))
f()
else
20
}
}
编辑 2:邮件列表研究:
在邮件列表中阅读此interesting post,它基本上声明=> T 实现为() => T。引用:
首先,看看
f: => Boolean虽然这被称为“按名称参数”,但实际上是作为
Function0实现的,
f: () => Boolean只是两端使用不同的语法。
现在我对this answer 更加困惑,它明确指出两者是不同的。
问题:
- Scala 如何区分
bar和baz?两者的方法签名(不是实现)在反编译代码中是相同的。 - 两种方案的差异不会被持久化到编译后的字节码中吗?
- 反编译的代码是否不准确?
-
在编辑 1 之后添加:我发现 scalac 语法树确实有所不同,
bar具有_root_.scala.<byname>[Int]类型的第二个参数。它有什么作用?任何解释、scala source 中的指针或等效的伪代码都会有所帮助。 -
参见上面的编辑 2:引用的块是否正确?例如,
=> T是Function0的特殊子类吗?
【问题讨论】:
-
为什么这些类型在 JVM 字节码中的表示方式很重要?它们在 Scala 代码中有所不同,这很重要(除非您尝试从 Java 调用 Scala,但这不是您要的)。
-
因为 Scala 就是这样工作的。它将 scala 代码编译为 .class 文件并在 JVM 中执行。因此.class 文件应该有必要和充分的信息。 Scala 源文件根本不重要。参见linked question,删除
ParamTest.scala源文件后可以重现调用foo(baz,100)的错误。 -
这个问题的另一个动机是this answer,其中最受好评的评论 (160) 断言,我引用:“此外,“按名称调用”与名称无关。
=> Int是与Int不同的类型;它是“没有参数的函数会生成一个 Int”,而不仅仅是 Int”。但正如我们在上面看到的,事实并非如此。 call-by-name 与惰性求值有关,并不是说它是一个没有参数的函数... -
您将编程语言与字节码混淆了。如果你翻译 C++,你会得到一堆指令,比如
mov bx, ax,它们几乎不会告诉你关于原始代码的信息。 Call-by-name 代表惰性求值,它不是 Scala 术语中的函数,但字节码不会告诉你这一点。