2012年7月15日日曜日

[Scala] apply

val a1 : Array[Int] = new Array(5);
val a2 : Array[Int] = Array(5);

誰だってこのコードを見たら、「 Scala って new は省略できるんだ。」って思う(と思う)。

val a1 : Array[Int] = new Array(5);
val a2 : Array[Int] = Array(5);
println("a1 size = " + a1.length);
println("a2 size = " + a2.length);


a1 size = 5 a2 size = 1

で、「あれっ」となる。

val a1 : Array[Int] = new Array(5);
val a2 : Array[Int] = Array(5);
println("a1.toSeq = " + a1.toSeq);
println("a2.toSeq = " + a2.toSeq);


a1.toSeq = WrappedArray(0, 0, 0, 0, 0) a2.toSeq = WrappedArray(5)

できた Array の中身はこんな感じになっている。
(ArrayそのもののtoStringは「a1 = [I@7f188439」のようになってしまう。)

「 Array(5)」で省略されていたのは、apply という名のクラスメソッド。
省略せずに書くと、「Array.apply(5)」になる。


クラスに対する Apply


クラス名に「(」が続いた場合には、apply メソッドが呼び出される。
通常はオブジェクトの生成(ファクトリ)として使用されるがコンストラクタと違って、 そのクラスのオブジェクトを返す必要はない。実際、List.apply も

val l : List[Int] = List(1,2,3); 
println("List.apply  = " + l.getClass());


List.apply = class scala.collection.immutable.$colon$colon

と謎なクラスを返している。「val l : List[Int]」に代入できていることから List の派生クラスになるわけなのだが、実際には派生クラスを返す必要さえない。
なのでこういうこともできてしまう。

object ApplySample {
  def main(args : Array[String]) {
    val s : String = ApplySample(10);
    println(s)
  }
  def apply(a: Int) : String = "hello." + a;
}

hello.10

引数をとるようにしたが引数がない場合でも同様なことができる。
apply メソッドはそのクラスまたはその派生クラスのオブジェクトを返すのが慣例になっているそうなので、 あまり変なことはしないほうがいいとは思う。


オブジェクトに対するApply


オブジェクトに対しても同様で、オブジェクトに括弧を続けるとオブジェクトに定義されている apply が呼ばれる。

val a : Array[Int] = Array(100,200,300);
println(a(1));

200

これで配列の添字アクセスのようなことができるようになっている。

関数オブジェクトに対する呼び出しも apply が省略されている。

val f: (Int) => Int = (x: Int) => x * 2;
println(f(10))
println(f.apply(10))

既存の関数をオブジェクト化した場合も同じ。

println(Math.sin(Math.Pi / 2));
println((Math.sin _).apply((Math.Pi / 2)));

ただし、直接に関数を実行したときには、apply が省略されているわけではないので、 「 Math.sin.apply( Math.Pi ) 」と書くことはできない。



いずれにしても、apply は Scala の文法的には特別扱いになっているが、メソッドそのものが特別なわけではない。


0 件のコメント:

コメントを投稿