型のvarianceは型の情報のみ持つ時、「アップキャストは可能だがダウンキャストは原則不可能」ということから使い分けが出来る。
アップキャストは可能だがダウンキャストは原則不可能
case class Life(age: Int)
case class Animal(age: Int, size: String) extends Life(age)
case class Human(age: Int, size: String, language: String) extends Animal(age, size)
という継承関係を考える。
AnimalもHumanも、ageをパラメタとして持っているため、Lifeを読み出すことは出来る。
しかしながら、LifeからAnimalを読み出そうと思ったら、sizeおよびlanguageが足らないため(型情報のみからでは)不可
Animal ---> Life OK
Life -X-> Animal NG
矢印は型の変換を表す。
具体的な情報を抽象的な情報に落とすことは出来るが、逆は出来ない。
高校の集合論の記法で書くと、それぞれの集合に対して
Human ⊂ Animal ⊂ Life
の関係が成り立つ。
反変と共変、型の変換元と変換先
上で見た関係より、暗黙的に変換可能な型の関係としては
(具体的) Human ---> Animal ---> Life (抽象的)
となる。
共変
ここで StringなりJsonなり適当なリソースから型T
の値を読み込む Read[T]
を仮定する。
このとき、
Read[Human]
はHuman
が得られるが、
Human ---> Animal
より、 Read[Human]
は(変換することで) Animal
も得ることが出来るため、
Read[Animal]
でもあることがわかる。
よって
Read[Human] ⊂ Read[Animal]
したがって、
継承関係
Read[Human] ⊂ Read[Animal] ⊂ Read[Life]
が存在する。
これは
Human ⊂ Animal ⊂ Life
と同じ向きの継承関係となっている(共変)
反変
ここで StringなりJsonなり適当なリソースへ型T
の値を書き込む Write[T]
があったとする。
このとき、
Write[Life]
はLife
を書き込める。
しかし、
Animal ---> Life
より、 Write[Life]
は(変換することで) Animal
も書き込むことが出来るため、
Write[Animal]
でもある。
したがって、
継承関係
Write[Human] ⊃ Write[Animal] ⊃ Write[Life]
が存在する。
これは
Human ⊂ Animal ⊂ Life
と逆向きの継承関係となっている(反変)
まとめ
以上のように、管理している型から別のものに書き込む際は反変、読み込む場合は共変関係が成り立つ。
そしてそれは型内部でアップキャストは可能だがダウンキャストは原則不可能という関係から導くことが出来る。