为什么scalac会生成额外/包装闭包

3

首先,考虑以下代码:

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)

foreachexception之间有额外的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


("anonfuns"可能指匿名函数)

Scala版本2.9.2.rdev-2769-2011-12-13-g2dd83da(Java HotSpot(TM) 64-Bit Server VM,Java 1.6.0_25)。 - tuxSlayer
1个回答

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非常擅长消除这种问题,所以我不会过多担心它。
编辑:当然,在你的第二个例子中,你使用了一个def,而anonfun是包装那个def成一个函数对象的类。这当然是必需的,因为foreach需要一个Function1。你必须以某种方式生成那个Function1

哦...我明白了。但是使用(f: A => Any)作为foreach()的参数不是更好吗?这样可以避免这种生成。 - tuxSlayer
@tuxSlayer - 那还是不行。A => Any会被擦除为Object => Object,而这已经是正在使用的了。 - Rex Kerr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接