読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

日曜にやったことぺたり

Scala Java Tips

色んなコードを逆コンパイルして、実装眺めてた。
ても、結構色んなパターンやったくせにログが残ってないので、やったことの記憶を辿って一部だけ。
(貼りつけるだけで面倒)

まずは object の実装。

package sample

object HelloWorld {
  def tak( x:Int , y:Int , z:Int ):Int = {
    if (x <= y) y else ( tak( tak( x - 1 , y , z ) , tak( y - 1 , z , x ) , tak( z - 1 , x , y ) ))
  }

  def main(args:Array[String]) {
    println(tak(20,10,5))
  }
}

これをコンパイルすると、「HelloWorld$.class」「HelloWorld.class」の二種類が生成される。
さらに、これを逆コンパイルした結果がコレ。

package sample;

// HelloWorld.class

public final class HelloWorld
{

    public static final void main(String args[])
    {
        HelloWorld$.MODULE$.main(args);
    }

    public static final int tak(int i, int j, int k)
    {
        return HelloWorld$.MODULE$.tak(i, j, k);
    }
}

$ 付きのヤツがコレ

package sample;

import scala.Predef$;
import scala.ScalaObject;
import scala.runtime.BoxesRunTime;

public final class HelloWorld$
    implements ScalaObject
{

    public int tak(int x, int y, int z)
    {
        do
        {
            if(x <= y)
                return y;
            z = tak(z - 1, x, y);
            y = tak(y - 1, z, x);
            x = tak(x - 1, y, z);
        } while(true);
    }

    public void main(String args[])
    {
        Predef$.MODULE$.println(BoxesRunTime.boxToInteger(tak(20, 10, 5)));
    }

    private HelloWorld$()
    {
    }

    public static final HelloWorld$ MODULE$ = this;

    static 
    {
        new HelloWorld$();
    }
}

確かにシングルトン。で、Java的なインターフェースクラスとして HelloWorld を作り、Scala の内部的な実装は、HelloWorld$ に集約している。Java側カラ見れば単なる static クラスとして見えるわけだ。
再帰呼び出し部分に、若干の最適化がかかっているのが分かる。


今度は trait

trait Taks {
  def tak( x:Int , y:Int , z:Int ):Int = {
    if (x <= y) y else ( tak( tak( x - 1 , y , z ) , tak( y - 1 , z , x ) , tak( z - 1 , x , y ) ))
  }
}

object HelloWorld extends Taks {

  def main(args:Array[String]) {
    println(tak(20,10,5))
  }
}

今度は4つ。まとめて表示。

// HelloWorld.class
package sample;

public final class HelloWorld
{

    public static final int tak(int i, int j, int k)
    {
        return HelloWorld$.MODULE$.tak(i, j, k);
    }

    public static final void main(String args[])
    {
        HelloWorld$.MODULE$.main(args);
    }
}

// HelloWorld$.class
package sample;

import scala.Predef$;
import scala.ScalaObject;
import scala.runtime.BoxesRunTime;

public final class HelloWorld$
    implements Taks, ScalaObject
{

    public volatile int tak(int x, int y, int z)
    {
        return Taks.class.tak(this, x, y, z);
    }

    public void main(String args[])
    {
        Predef$.MODULE$.println(BoxesRunTime.boxToInteger(tak(20, 10, 5)));
    }

    private HelloWorld$()
    {
        Taks.class.$init$(this);
    }

    public static final HelloWorld$ MODULE$ = this;

    static 
    {
        new HelloWorld$();
    }
}

// Taks.class
package sample;

import scala.ScalaObject;

public interface Taks
    extends ScalaObject
{

    public abstract int tak(int i, int j, int k);
}

// Taks$Classs.class
package sample;

public abstract class Taks$class
{

    public static int tak(Taks $this, int x, int y, int z)
    {
        return x > y ? $this.tak($this.tak(x - 1, y, z), $this.tak(y - 1, z, x), $this.tak(z - 1, x, y)) : y;
    }

    public static void $init$(Taks taks)
    {
    }
}

trait は interface と実装に分離され、trait を実際に使用しているコード(HelloWorld$)にて、呼び出しを定義してる。
これを敢えて object 以外で trait するとどうなるか?

package sample

import sample.{AutoTak, Taks}

trait Taks {
  def tak( x:Int , y:Int , z:Int ):Int = {
    if (x <= y) y else ( tak( tak( x - 1 , y , z ) , tak( y - 1 , z , x ) , tak( z - 1 , x , y ) ))
  }
}

class AutoTak extends Taks {
  def autoTak = tak(20,10,5)
}

object HelloWorld extends Taks {

  def main(args:Array[String]) {
    val auto = new AutoTak
    println(auto.autoTak)
  }
}

すると、

// AutoTak.java
package sample;

import scala.ScalaObject;

public class AutoTak
    implements Taks, ScalaObject
{

    public volatile int tak(int x, int y, int z)
    {
        return Taks.class.tak(this, x, y, z);
    }

    public int autoTak()
    {
        return tak(20, 10, 5);
    }

    public AutoTak()
    {
        Taks.class.$init$(this);
    }
}

// Taks.class
package sample;

import scala.ScalaObject;

public interface Taks
    extends ScalaObject
{

    public abstract int tak(int i, int j, int k);
}

// Taks$class.class
package sample;

public abstract class Taks$class
{

    public static int tak(Taks $this, int x, int y, int z)
    {
        return x > y ? $this.tak($this.tak(x - 1, y, z), $this.tak(y - 1, z, x), $this.tak(z - 1, x, y)) : y;
    }

    public static void $init$(Taks taks)
    {
    }
}

ちょっと待て、static になってんぞ、、、、。
まぁ、基本が定数の関数型なら不都合は無いんだろうけど、Javaと共用するときに知らんと死ぬね。

そして、高階関数をやってみた。すると、逆コンパイラjadデコンパイルに失敗した。
scala ソースは以下。

class SubHello {
  def hello = (str:String) => println("Hello, %s!".format(str))
}

object HelloWorld {

  def main(args:Array[String]) {
    val hello = new SubHello
    hello.hello("Azalea")
  }
}

HelloWorld.class は端折って、呼び出し口から。

import scala.Function1;
import scala.ScalaObject;

// Referenced classes of package sample:
//            SubHello

public final class HelloWorld$
    implements ScalaObject
{

    public void main(String args[])
    {
        SubHello hello = new SubHello();
        hello.hello().apply("Azalea");
    }

    private HelloWorld$()
    {
    }

    public static final HelloWorld$ MODULE$ = this;

    static 
    {
        new HelloWorld$();
    }
}
import scala.*;
import scala.collection.immutable.StringLike;
import scala.runtime.AbstractFunction1;
import scala.runtime.BoxedUnit;

public class SubHello
    implements ScalaObject
{

    public Function1 hello()
    {
        return new Serializable() {

            public final void apply(String str)
            {
                Predef$.MODULE$.println(Predef$.MODULE$.augmentString("Hello, %s!").format(Predef$.MODULE$.genericWrapArray(((Object) (new Object[] {
                    str
                })))));
            }

            public final volatile Object apply(Object v1)
            {
                apply((String)v1);
                return BoxedUnit.UNIT;
            }

            public static final long serialVersionUID;

            static 
            {
                0L;
                serialVersionUID = 0L;
            }

        }
;
    }

    public SubHello()
    {
    }
}
import scala.Predef$;
import scala.Serializable;
import scala.collection.immutable.StringLike;
import scala.runtime.AbstractFunction1;
import scala.runtime.BoxedUnit;

// Referenced classes of package sample:
//            SubHello

public final class SubHello$$anonfun$hello$1 extends AbstractFunction1
    implements Serializable
{

    public final void apply(String str)
    {
        Predef$.MODULE$.println(Predef$.MODULE$.augmentString("Hello, %s!").format(Predef$.MODULE$.genericWrapArray(((Object) (new Object[] {
            str
        })))));
    }

    public final volatile Object apply(Object v1)
    {
        apply((String)v1);
        return BoxedUnit.UNIT;
    }

    public static final long serialVersionUID;

    static 
    {
        0L;
        serialVersionUID = 0L;
    }

    public SubHello$$anonfun$hello$1(SubHello $outer)
    {
    }
}

なるほど、Serializable オブジェクトとして関数をラップして hello では返していると。
で、呼び出しは Serializable IF経由で apply して実行するわけですね。
そしてパターンマッチ。

class SubHello {
  def hello(typeName:String) = typeName match {
    case "jp" => "おはようございます。"
    case _ => "Hello."
  }
}
public class SubHello
    implements ScalaObject
{

    public String hello(String typeName)
    {
        String s = typeName;
        s;
        String s1 = "jp";
        if(s != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        if(s1 == null) goto _L4; else goto _L3
_L2:
        s1;
        equals();
        JVM INSTR ifeq 30;
           goto _L4 _L3
_L4:
        "\u304A\u306F\u3088\u3046\u3054\u3056\u3044\u307E\u3059\u3002";
          goto _L5
_L3:
        "Hello.";
_L5:
        return;
    }

    public SubHello()
    {
    }
}

ぎゃわー。goto 実装かよw
確かに、case => の右辺は式だからのう、、、、stack消費を抑える目的があるように見える。