为什么scalac会产生额外的/包裹闭包

时间:2012-02-22 14:39:50

标签: scala compiler-construction closures

首先。请考虑以下代码

scala> val fail = (x: Any) => { throw new RuntimeException }
fail: Any => Nothing = <function1>

scala> List(1).foreach(fail)
java.lang.RuntimeException
    at $anonfun$1.apply(<console>:7)
    at $anonfun$1.apply(<console>:7)
    at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)

foreach和exception之间还有额外的anonfun。一个值应该是fail本身的值(类Function1 []的对象),但第二个来自哪里?

foreach签名采用此功能:

def foreach[U](f: A => U): Unit 

那么,第二个目的是什么?

其次,请考虑以下代码:

scala> def outer() {
     |   def innerFail(x: Any) = { throw new RuntimeException("inner fail") }
     | 
     |   Set(1) foreach innerFail
     | }
outer: ()Unit

scala> outer()
java.lang.RuntimeException: inner fail
    at .innerFail$1(<console>:8)
    at $anonfun$outer$1.apply(<console>:10)
    at $anonfun$outer$1.apply(<console>:10)
    at scala.collection.immutable.Set$Set1.foreach(Set.scala:86)

还有两个额外的anonfuns ......他们真的需要吗? :-E

1 个答案:

答案 0 :(得分:4)

让我们看一下字节码。

object ExtraClosure {
  val fail = (x: Any) => { throw new RuntimeException }
  List(1).foreach(fail)
}

我们在(单个)匿名函数中找到:

public final scala.runtime.Nothing$ apply(java.lang.Object);
  Code:
   0:   new #15; //class java/lang/RuntimeException
   3:   dup
   4:   invokespecial   #19; //Method java/lang/RuntimeException."<init>":()V
   7:   athrow

public final java.lang.Object apply(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokevirtual   #27; //Method apply:(Ljava/lang/Object;)Lscala/runtime/Nothing$;
   5:   athrow

所以它毕竟不是一个额外的关闭。我们有一个方法重载了两个不同的返回值(这对于JVM来说是完全可以的,因为它将所有参数的类型视为函数签名的一部分)。函数是通用的,因此它必须返回对象,但是您编写的代码专门返回Nothing,它还会创建一个返回您期望的类型的方法。

有各种各样的方法,但没有一个没有他们的缺点。这是JVM非常擅长的事情,但是,我不会太担心它。

编辑:当然,在第二个示例中,您使用了defanonfun是在函数对象中包装def的类。这当然是必需的,因为foreach需要Function1。你必须以某种方式生成Function1