Scala ひと巡り : 変位指定 (Variances)

原ページ

Scala は、ジェネリッククラスの型パラメータ [16]の変位指定アノテーションをサポートします。Java5 (aka.JDK 1.5[58])と対照して、クラス抽象を定義するときに変位指定アノテーションを加えることができます。他方、Java5 では、変位指定アノテーションはクラス抽象を使うときにクライアントが与えます。(訳注:Scalaでは静的に表現できるということ)

ジェネリッククラスについてのページに、ミュータブル(更新可能)なスタックの例がありました。クラス Stack[T] によって定義された型は、型パラメータについて非変のサブ型付けになると説明しました。それによりクラス抽象の再利用を制限できます。今度はこの制限を持 たないスタックの関数型(つまり、イミュータブル(更新不可)な)実装を導きます。これが、明白ではない方法で多相的メソッド [25]、下限型境界 [19]、そして共変の型パラメータアノテーションの使用を結びつける、進んだ例であることに注意してください。さらにまた、スタック要素を明示的なリンクなしでチェインするために、内部クラス [20]を利用します。

class Stack[+A] { def push[B >: A](elem: B): Stack[B] = new Stack[B] { override def top: B = elem override def pop: Stack[B] = Stack.this override def toString() = elem.toString() + " " + Stack.this.toString() } def top: A = error("no element on stack") def pop: Stack[A] = error("no element on stack") override def toString() = "" } object VariancesTest extends Application { var s: Stack[Any] = new Stack().push("hello"); s = s.push(new Object()) s = s.push(7) Console.println(s) }

アノテーション +T は、型 T が共変の位置でだけ使われると宣言します。同様に、-T は、T が反変の位置でだけ使われると宣言します。共変の型パラメータなので、この型パラメータについて共変のサブ型関係を得ます。この例では、もし T が S のサブ型なら、このことは、Stack[T] が Stack[S]のサブ型であることを意味します。 - とタグ付けられた型パラメータについては、反対のことが当てはまります。スタックの例では、メソッド push を定義できるようにするために、反変の位置で共変型パラメータ T を使う必要があります。スタックは共変のサブ型付けにしたいのですから、トリックを使います。メソッド push のパラメータ型上で抽象化します。push の型変数の下限境界として要素型 T を使う多相的メソッド [25]を 得ます。これには、その共変型パラメータとしての宣言と協調する T の変位指定を持ち込む効果があります。今、スタックは共変です。しかしこのソリューションによれば、たとえば、整数スタック上に文字列をプッシュすること が可能となります。戻り値は型 Stack[Any] のスタックです。;整数スタックを期待するコンテキスト中で戻り値を使う場合にだけ、実際にエラーを検出します。そうでない場合、より汎用的な要素型のス タックを得ます。