Почему скаляр создает дополнительные / закрывающие - PullRequest
3 голосов
/ 22 февраля 2012

Во-первых. Рассмотрим следующий код

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 и исключением. Предполагается, что одним из них будет само значение 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)

Есть два дополнительных анонфуна ... они действительно нужны? : -E

1 Ответ

4 голосов
/ 22 февраля 2012

Давайте посмотрим на байт-код.

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.

...