Scala编译:匿名函数

时间:2016-12-06 15:38:01

标签: scala lambda compilation

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);
    }
}

3 个答案:

答案 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表达式与方法引用是另一个例子。