日曜にやったことぺたり
色んなコードを逆コンパイルして、実装眺めてた。
ても、結構色んなパターンやったくせにログが残ってないので、やったことの記憶を辿って一部だけ。
(貼りつけるだけで面倒)
まずは 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消費を抑える目的があるように見える。