the industrial

都内で働くITエンジニアの日記

Scalaのforループ処理をデコンパイルしてみた

結構前の事なんだけど

  1. Scalaの言語仕様についていろいろ勉強していた
  2. Scalaのforループにて利用する「<-」で謎の挙動を発見
  3. Scala勉強会で質問
  4. なんとなく理解
  5. 結局忘れてた

って事があったので、今一度しっかりと理解する為に例となるプログラムを書いて、デコンパイルをして調査してみたので、メモしておく。

デコンパイルについて

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)

つっこみあったらよろしくおねがいします。