首先。考虑以下代码
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 和异常之间还有额外的 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
让我们看一下字节码。
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 非常擅长消除这种类型的事情,所以我不会太担心它。
编辑:当然,在第二个示例中,您使用了
def
,而 anonfun
是将 def
包装在函数对象中的类。 这当然是需要的,因为 foreach
需要 Function1
。 你必须以某种方式生成那个 Function1
。