実際にあった scala.App の怖い間違い (解説編)

まだの方は [実際にあった scala.App の怖い間違い](http://qiita.com/kawachi/items/b272d8c49ccf7bddfb99) からご覧ください。

## 何が変なの？

最後のコードを repl で動かしてみます。

```scala
scala> :paste
// Entering paste mode (ctrl-D to finish)

trait SuperMain extends App {
  def printClassName(): Unit

  println("Start")
  printClassName()
  println("Finish")
}
object Main1 extends SuperMain {
  def printClassName(): Unit = println("Main1")
}
object Main2 extends SuperMain {
  def printClassName(): Unit = println("Main2")
}

// Exiting paste mode, now interpreting.

defined trait SuperMain
defined object Main1
defined object Main2

scala> Main1.main(Array())
Start
Main1
Finish

```

ちゃんと動いている様に見えます。ここで続けてもう一度 `Main1.main(Array())` を実行してみます。

```scala
scala> Main1.main(Array())
// なにも表示されない！？
```

2度目以降の `main()` 呼び出しでは何も表示されません。 `SuperMain` にまとめる前の次のコードでは動いていたのに。

```scala
scala> :paste
// Entering paste mode (ctrl-D to finish)

object Main1 extends App {
  def printClassName(): Unit = println("Main1")

  println("Start")
  printClassName()
  println("Finish")
}

object Main2 extends App {
  def printClassName(): Unit = println("Main2")

  println("Start")
  printClassName()
  println("Finish")
}

// Exiting paste mode, now interpreting.

defined object Main1
defined object Main2

scala> Main1.main(Array())
Start
Main1
Finish

scala> Main1.main(Array()) // 2度目も動く
Start
Main1
Finish

```

`SuperMain` で共通化したコードでは、実は `Main1` object を参照したタイミングで `println()` が実行されています。 `main()` を実行しても何も起こりません。

## なぜそうなるか？


なぜ `main()` 呼び出し時に `println()` が実行されないか探るために、 [App.scala](https://github.com/scala/scala/blob/v2.12.2/src/library/scala/App.scala) を見てみましょう。注目したい点だけを抜き出すと次のようになっています。

```scala
trait App extends DelayedInit {

  private val initCode = new ListBuffer[() => Unit]

  override def delayedInit(body: => Unit) {
    initCode += (() => body)
  }

  def main(args: Array[String]) = {
    // ...
    for (proc <- initCode) proc()
    // ...
  }
}
```

`DelayedInit` を継承し、 `delayedInit()` で `initCode` に処理をあつめ、 `main()` で実行しています。

[Scala言語仕様 5.1](http://www.scala-lang.org/files/archive/spec/2.12/05-classes-and-objects.html#templates) の Delayed Initialization に `DelayedInit` の説明があります。

> The initialization code of an object or class (but not a trait) that follows the superclass constructor invocation and the mixin-evaluation of the template's base classes is passed to a special hook, which is inaccessible from user code. Normally, that hook simply executes the code that is passed to it. But templates inheriting the scala.DelayedInit trait can override the hook by re-implementing the delayedInit method,

「object または class の初期化コード (ただし trait は除く)は、」 という書き出しが目に止まったでしょうか？

そうです。trait の初期化コードは `delayedInit()` には渡されない。 `delayedInit()` に渡されなければ、`App.scala` で `initCode` に追加されることもなく `main()` 呼び出し時に実行されることもありません。

## どうすれば良かったか?

> 似たような処理が複数箇所にあるのは良くないので、共通の親クラス(`SuperMain`)にまとめます。

と言いながら `trait SuperMain` にしたのが原因です。 class なら大丈夫なはずです。試してみましょう。

```scala
scala> :paste
// Entering paste mode (ctrl-D to finish)

// trait ではなく class にする
abstract class SuperMain extends App {
  def printClassName(): Unit

  println("Start")
  printClassName()
  println("Finish")
}
object Main1 extends SuperMain {
  def printClassName(): Unit = println("Main1")
}
object Main2 extends SuperMain {
  def printClassName(): Unit = println("Main2")
}

// Exiting paste mode, now interpreting.

defined class SuperMain
defined object Main1
defined object Main2

scala> Main1.main(Array())
Start
Main1
Finish

scala> Main1.main(Array()) // 二度目も実行される
Start
Main1
Finish

```

怖いですね。こんなこと気にしなければいけないくらいなら、自分は `extends App` の代わりに `def main(args: Array[String]): Unit = {}` と書きます。たいしてタイプ数変わらないしね。