Scalaのforループ処理をデコンパイルしてみた
結構前の事なんだけど
って事があったので、今一度しっかりと理解する為に例となるプログラムを書いて、デコンパイルをして調査してみたので、メモしておく。
デコンパイルについて
jadというものを使っています。
JAD Java Decompiler Download Mirror
謎だった挙動
初め、下記の様に”Javaライク”にforループ処理を書いていた。
すると、実行結果にてnullの値を参照せずヌルポエラーが起きなかった。
val targetList: List[String] = List("いち", null, "", "よん") for (target:String <- targetList) { println(target.length()) } // 実行結果 // 2 // 0 // 2
むむ?っと思い、この時点でなんとなく「<-」が上手いことやってんだなーという様に理解。
しかし、逆にヌルポが出る場合も把握しておきたいと思い、色々書きなおしていてたら、どうやら「target:String」で型を指定しているからヌルポエラーが起きないのだと分かった。
以下はStringの型指定を省いた場合。
val targetList: List[String] = List("いち", null, "", "よん") for (target <- targetList) { println(target.length()) } // 実行結果 // 2 // Exception in thread "main" java.lang.NullPointerException // at test.ForTest$$anonfun$1.apply(ForTest.scala:6) // at test.ForTest$$anonfun$1.apply(ForTest.scala:5) // at scala.collection.immutable.List.foreach(List.scala:318) // at test.ForTest$delayedInit$body.apply(ForTest.scala:5) // at scala.Function0$class.apply$mcV$sp(Function0.scala:40) // at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) // at scala.App$$anonfun$main$1.apply(App.scala:71) // at scala.App$$anonfun$main$1.apply(App.scala:71) // at scala.collection.immutable.List.foreach(List.scala:318) // at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32) // at scala.App$class.main(App.scala:71) // at test.ForTest$.main(ForTest.scala:3) // at test.ForTest.main(ForTest.scala)
なんだか悶々としていたので勉強会で質問。
頂いた答えは、「『withFilter』が効いてるから。」とのこと。
なるほど、確かにStringを型指定しているのだから当たり前か、と理解。
じゃあどうせなら、実際にはどういう風にコンパイルされるのかを、デコンパイルしてみた。
デコンパイル結果
型を指定しない普通(?)のforループ
// デコンパイル前 val target1List: List[String] = List("いち", null, "", "よん") for (target1 <- target1List) { println(target1) }
// デコンパイル後 target1List = List$.MODULE$.apply(Predef$.MODULE$.wrapRefArray((Object[])(new String[] {"\u3044\u3061", null, "", "\u3088\u3093"}))); target1List().foreach(new Serializable() { public final void apply(String target1) { Predef$.MODULE$.println(target1); } public final volatile Object apply(Object v1) { apply((String)v1); return BoxedUnit.UNIT; } }
型を指定した場合のforループ
// デコンパイル前 val targetList2: List[String] = List("いち", null, "", "よん") for (target2: String <- targetList2) { println(target2) }
// デコンパイル後 targetList2 = List$.MODULE$.apply(Predef$.MODULE$.wrapRefArray((Object[])(new String[] {"\u3044\u3061", null, "", "\u3088\u3093"}))); targetList2().withFilter(new Serializable() { public final boolean apply(String check$ifrefutable$1){ String s = check$ifrefutable$1; boolean flag; if(s != null) flag = true; else flag = false; return flag; } public final volatile Object apply(Object v1) { return BoxesRunTime.boxToBoolean(apply((String)v1)); } }).foreach(new Serializable() { public final void apply(String target2) { Predef$.MODULE$.println(target2); } public final volatile Object apply(Object v1) { apply((String)v1); return BoxedUnit.UNIT; } });
withFilterされているのが確認できた。
さいごに
こちらも参考にさせていただいております。
Scala for実体メモ(Hishidama's Scala for-convert Memo)
つっこみあったらよろしくおねがいします。