読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

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じゃないよー!!

このような制限はJavaにもあります。Scalaもデフォルトでは制限されていますが、型パラメータの設定で変更できます。

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 // 代入できる!