= 名前渡しパラメータ = == 基本 == 引数の定義時に「=>」をつけると名前渡しパラメータになる。どのようなものかは以下を参照 {{{ object Sample { def foo = { println("foo") 123 } /** 値渡し */ def byValue(a: Int) = { println("byValue entry") val r = a + a println("byValue exit") r } /** 名前渡し */ def byName(a: => Int) = { // =>をつけてるだけ println("byName entry") val r = a + a println("byName exit") r } def main(args: Array[String]) { println(byValue(foo)) println("") println(byName(foo)) } } }}} この実行結果は {{{ foo byValue entry byValue exit 246 byName entry foo foo byName exit 246 }}} 両方ともfooを引数として渡しているが、 * 値渡しの場合は、評価された後の値が渡されており、その時にfooが一度だけ呼び出されている。 * 名前渡しの場合は、渡されるときには評価されず、必要なときに何度もfooが呼び出されている。 == 関数渡しとは異なる(らしい) == 先のbyNameの定義を、明示的な「関数渡し」とすると、こうなる。 {{{ def byName(a:() => Int) = { println("byName entry") val r = a() + a() // 評価時にカッコが必要 println("byName exit") r } }}} そしてbyName呼び出し側もこう書く必要がある。 {{{ println(byName(() => foo)) }}} ※あるいは、「def foo」を「def foo()」にすればよい。 しかし、呼び出す関数に引数を与えたい場合にはこの書き方が必須である。つまり、 {{{ object Sample { def double(value: Int): Int = value * 2 def higher(f: (Int) => Int): Int = { f(123) } def main(args: Array[String]) { println(higher(double)) } } }}} 結果は {{{ 246 }}} == 名前渡しの利用例1 == 一般に名前渡しは「新たな制御構造」を作成する場合に便利に利用できるとのことであるが、以下のように「ブロックを実行して、その値を返すか、例外が起こったときには一律に例外処理をする」といった用途にも使用できる。 {{{ object Sample { // 不適当だが呼び出したいメソッドたち。おそらくレガシーコード ///////////// def notZero(value: Int): Int = { println("notZero called") if (value != 0) value else throw new NullPointerException("ERROR") // とても不適当な例外 } def nothing() { // このメソッドはUnitを返す println("nothing called") } // 公開したいメソッドたち。おそらく新しいAPI /////////////////////// def something(value: Int): Int = processThrowable { notZero(value) } def anything() = processThrowable { nothing } // レガシーコードの結果値を返すか、あるいは例外を適切に変換する //////// private def processThrowable[T](block: => T): T = { try { block } catch { // 不適当な例外を変更する case re: NullPointerException => { throw new IllegalArgumentException(re.getMessage()) } } } def main(args: Array[String]) { println(something(12)) try { something(0) } catch { case ia: IllegalArgumentException => { println("exception:" + ia.getMessage()) } } anything() } } }}} 実行結果は {{{ notZero called 12 notZero called exception:ERROR nothing called }}} == 名前渡しの利用例2 == ロギングに使う。 {{{ trait Logging { def log(message: => String) { if (true) { // どこかのフラグを見る println(message) } } } object LogTest extends Logging { def main(args: Array[String]) { log("num args:" + args.length) if (args.length > 0) { log("first arg:" + args(0)) } } } }}} Javaの場合は常に値渡しなので、上記のように記述すると、logメソッドの引数は必ず評価されてしまうため、 次のようにしないと、不必要な評価が発生してしまう。 {{{ if (isLogEnabled()) { log("message " ...) } }}} Scalaの場合、本当にログが必要なときにだけ評価が行われる。 参考1:[[http://stackoverflow.com/questions/2018528/logging-in-scala]] 参考2:[[http://stackoverflow.com/questions/978252/logging-in-scala]]