Variance Annotations
型パラメータに「+」「-」をつけることで、パラメータ化されたクラスの代入制限を少し緩くできます。
パラメータ化されたクラスの代入制限とは?
次のような、型パラメータを受け取るクラスがあるとして、
// 型パラメータを受け取るクラス class Holder[A](value:A) { private var v:A = value // getter def get:A = { v } // setter def set(value:A):unit = { v = value } }
「AnyRef」と、その派生クラスである「String」でそれぞれインスタンスを作成した場合、両者に代入互換性はありません。
// 「AnyRef」と、その派生クラスである「String」でそれぞれインスタンスを作成 var stringHolder = new Holder[String]( "foo" ) var anyRefHolder = new Holder[AnyRef]( "foo" ) // StringはAnyRefの派生クラスであるが、 // Holder[String]はHolder[AnyRef]へ代入できない。 // 逆も当然だめ。 //anyRefHolder = stringHolder // コンパイルエラー //stringHolder = anyRefHolder // コンパイルエラー
これは、仮に代入が可能であった場合、以下のような手順で不正な操作が可能であるためです。
var stringHolder = new Holder[String]( "foo" ) var anyRefHolder = new Holder[AnyRef]( new Object ) anyRefHolder = stringHolder anyRefHolder.set( new Object ) // Stringしか受け付けないはずのHolderにObjectが設定されてしまう!! stringHolder = anyRefHolder stringHolder.get() // Stringじゃないよー!!
Holder[String]をHolder[AnyRef]に代入できるようにする。
派生クラスが指定されたインスタンス(Holder[String])を親クラスが指定された変数(Holder[AnyRef]型の変数)に代入できるようにするには、Holderの型パラメータに「+」をつけます。
- これにより、Holderは「指定された型パラメータの派生クラスをパラメータにもつHolderと互換性がある」と定義され、代入が可能になります。
- ただし、互換性を維持できない操作はHolderで提供できなくなります。具体的には、次のような場合にコンパイルエラーになります。
- 引数で型パラメータを受け取るようなAPIが定義されている。
- 型パラメータ型のフィールドを持つ。
// Holder[String]をHolder[AnyRef]に代入できるHolder // 型パラメータに「+」をつける class CovariantHolder[+A]( value:A) { // 互換性を損なう恐れがある機能は提供できない。 //private var v:A = value // コンパイルエラー // getter // これはOK def get:A = { value } // setter //def set(value:A):unit = { v = value } // コンパイルエラー } var stringHolder = new CovariantHolder[String]( "foo" ) var anyRefHolder = new CovariantHolder[AnyRef]( "foo" ) anyRefHolder = stringHolder // 代入できる! //stringHolder = anyRefHolder // これはエラー。
Holder[AnyRef]をHolder[String]に代入できるようにする。
親クラスが指定されたインスタンス(Holder[AnyRef])を派生クラスが指定された変数(Holder[String]型の変数)に代入できるようにするには、Holderの型パラメータに「-」をつけます。
- これにより、Holderは「指定された型パラメータの親クラスをパラメータにもつHolderと互換性がある」と定義され、代入が可能になります。
- ただし、互換性を維持できない操作はHolderで提供できません。具体的には、次のような場合にコンパイルエラーになります。
- 戻り値で型パラメータを返すようなAPIが定義されている。
- 型パラメータ型のフィールドを持つ。
// Holder[AnyRef]をHolder[String]に代入できるHolder // 型パラメータに「-」をつける class ContravariantHolder[-A](value:A) { // 互換性を損なう恐れがある機能は提供できない。 //private var v:A = value // getter //def get:A = { v } // コンパイルエラー // setter // これはOK。 def set(value:A):unit = { } } var stringHolder = new ContravariantHolder[String]( "foo" ) var anyRefHolder = new ContravariantHolder[AnyRef]( "foo" ) //anyRefHolder = stringHolder // これはエラー。 stringHolder = anyRefHolder // 代入できる!