2011年8月31日水曜日

Scalaの変位


不変、共変と反変について。Scalaプログラミング入門のP.218について


  • Javaのクラスの配列は共変
  • あるクラスの配列を引数として、宣言している場合、サブクラスの配列を引数として渡せる。String[] をObject[]を代入可能
  • Javaのジェネリクスは共変ではなく、不変List<string> はList<Object>に代入できない。 Javaのクラスの配列は共変となっているが、型の安全性をコンパイル時に保証してくれない。

Scalaでは不変、共変、反変について指定することが可能

  • 不変:[T]
  • 共変:[+T]
  • 反変:[-T]

不変

ScalaではArray[T]は不変です。foo(a:Array[String])メソッドに渡せるのはArray[String]のみです。
ミュータブルの場合に、型引数を不変にすることが望ましい。

class Holder[T](var data: T)
    def add(in: Holder[Int]) { in.data = in.data + 1 }
    val h = new Holder(0)
    add(h)
    println(h.data)

Holder[Number]はDoubleも保持できます。

val nh = new Holder[Number](33.3d)
    def round(in: Holder[Number]) { in.data = in.data.intValue }
    println(round(nh))
    println(nh.data.getClass)

    val dh = new Holder(33.3d)
    round(dh)   &lt;--コンパイルエラー

共変

共変にする場合には、型引数の前に+をつける。
共変の型引数は読み取り専用のコンテナに便利
List[+T]と定義して、List[String]をList[Any]を引数としてメソッドに引数として渡すので、
Listのすべての要素はAnyである要件を満たしているため、Listは共変となります。Listの内容は変更できません。

サンプル

    class Getable[+T](val data: T)
    def get(in: Getable[Any]) { println("It's " + in.data) }
    val gs = new Getable("String")
    get(gs)

    def getNum(in: Getable[Number]) = in.data.intValue
    import java.lang.{ Double => JDouble }
    val gd = new Getable(new JDouble(33.3))
    println(getNum(gd))    
つまり、Getable[+T]で定義したときGetable[String]をGetable[Any]に代入することができる。
子クラスの型に対して親クラスの型に代入することができる。

反変

Putable[String]を引数とするメソッドに対してPutable[AnyRef]を渡して呼び出せます。
    class Putable[-T] {
      def put(in: T) { println("Putting " + in) }
    }
    def writeOnly(in: Putable[String]) { in.put("Hello") }
    val p = new Putable[AnyRef]
    writeOnly(p)
    //
    trait DS[-In, +Out] { def apply(i: In): Out }
    val t1 = new DS[Any, Int] { def apply(i: Any) = i.toString.toInt }
    def check(in: DS[String, Any]) = in("333")
    println(check(t1))


Putable[-T]で定義したときにPutable[AnyRef]をPutable[String]に代入
反変は共変の反対で親クラスの型に対して、子クラスの型に代入することができる。

変位ルール

  • ミュータブルなコンテナは不変
  • イミュータブルなコンテナは共変[+T]
  • 変換処理の入力は反変[-T]に、出力は共変[+T]

ScalaのFunctionNトレイトは反変の引数と共変の戻り値を持っている。


0 件のコメント:

コメントを投稿