8.3 下限境界 (Lower Bounds)

型パラメータの上限境界について見てきました。T <: U のような型パラメータ宣言では、型パラメータ T は型 U のサブタイプの範囲に制限されます。これと対照的なのが Scala における下限境界です。型パラメータ宣言 T >: S において、型パラメータ T は型 S の スーパータイプ の範囲に制限されます (上限境界と下限境界を組み合わせることもできます。T >: S <: U のように)。

下限境界を用いると、Stack の push メソッドを次のように一般化できます。

class Stack[+A] { def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x, this)

技術的には、これによって変位指定問題は解決します。なぜなら、型パラメータ A はいまや push メソッドの型パラメータではないからです。その代わり、メソッドの別の型パラメータの下限境界として現れ、その型パラメータは共変的な位置に分類されます。したがって Scala コンパイラは新しい push 定義を受け入れます。

実際、変位指定の技術的な問題を解決しただけではなく、push 定義を一般化しました。以前は、スタックの宣言された要素型に適合する型のみプッシュできました。今では、この型のサブタイプの要素もプッシュできますが、戻り値のスタックの型は適宜変化します。たとえば String のスタックに AnyRef をプッシュできますが、戻るスタックは String のスタックではなく AnyRef のスタックなのです!

要約すると、自分のデータ構造に変位指定アノテーションを付けることを躊躇すべきではありません。なぜならそれによって、リッチで自然なサブタイプ関係が得られるからです。コンパイラは健全性に関する潜在的な問題を見つけます。クラス Stack の push メソッドのようにコンパイラの推定が用心深すぎたとしても、問題となっているメソッドについて、有益な一般化をしばしば示唆します。