scala编译器是否有任何规范可以解释这种行为?
scala版本:2_10_6
代码示例
trait Service {
def process(s: String)
}
object ServiceImpl extends Service{
override def process(s: String): Unit = {
println(s)
}
}
object Register {
var serviceInst : Service = ServiceImpl
}
object Client1 {
def process1(l: List[String]): Unit ={
l.foreach(x => Register.serviceInst.process(x))
}
}
object Client2 {
def process1(l: List[String]): Unit ={
l.foreach(Register.serviceInst.process)
}
}
我假设process1和process2应该有类似的行为。但是,在编译/ decom之后
public final class Client1$$anonfun$process$1$$anonfun$apply$1 extends AbstractFunction1<String, BoxedUnit> implements Serializable {
public static final long serialVersionUID = 0L;
public final void apply(final String x$1) {
Register$.MODULE$.serviceInst().process(x$1);
}
}
public static final class Client2$$anonfun$process$1 extends AbstractFunction1<String, BoxedUnit> implements Serializable {
public static final long serialVersionUID = 0L;
private final Service eta$0$1$1;
public final void apply(final String s) {
this.eta$0$1$1.process(s);
}
}
答案 0 :(得分:2)
这是因为Scala编译器对Client2
中给出的方法执行eta-expansion,它通过生成直接在具体Function
实例上调用process
的{{1}}来工作。
以下是这些函数在转换为字节码之前的示例:
Service
答案 1 :(得分:1)
如果我们稍微重写serviceInst
,那就变得更有趣了:
object Register {
def serviceInst : Service = {
println("get service instance!!!")
ServiceImpl
}
}
然后执行:
Client1.process1(List("a","b"))
Client2.process1(List("a","b"))
显然结果不同:
1.
get service instance!!!
a
get service instance!!!
b
res0: Unit = ()
2.
get service instance!!!
a
b
res1: Unit = ()
解释是foreach函数参数的后面:
Client1
包含如下函数,它执行每个调用x => Register.serviceInst.process(x)
Client2
具有将要执行的函数process
,但首先serviceInst
即将被初始化。
答案 2 :(得分:0)
以下一行
l.foreach(x => Register.serviceInst.process(x))
在操作上等同于
l.foreach(Register.serviceInst.process)
第一个称为“点式”,而第二个称为“无点式”,或者更具体地说是“eta-conversion”,术语“点”指的是不存在的命名参数在第二种情况下。它们是两个不同的概念,因此编译方式不同。您可以用无点样式编写代码,Scala编译器在内部进行eta扩展,这就是您在Client2
的反编译代码中看到的内容。
eta conversion是来自Lambda Calculus的术语。如果lambda抽象的唯一目的是将其参数传递给另一个函数,则lambda是冗余的,可以通过eta转换/简化来剥离。 Java的Lambda表达式与方法引用是另一个例子。