はじめに
自分ではまだ使ったことない、ライブラリのソースコードでたまに見かける共変・反変についてどういった挙動をするのかは知っていたが、具体的な使い方のイメージがよくわからなかったので簡単なメモとして残した
共変
例
trait List[+A]
sealed trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
val dogs: List[Dog] = ???
val cats: List[Cat] = ???
val animals: List[Animal] = dogs
val animals2: List[Animal] = cats
状況
-
trait List[+A]
の宣言によってList
が生み出す型は、型のサブタイプの関係性を維持する - 具体例でいうと「
Dog
はAniaml
のサブタイプ =>List[Dog]
はList[Animal]
のサブタイプ」
使用されるケース
- outputの型として使うのが便利になるケース
- 上記の例でいうと最終的に
List
が得られる状況が大事で具体的なList[Dog]
という型に関心がないケース
反変
例
trait Show[-A] {
def show(value: A): Unit = ???
}
val animal: Show[Animal] = ???
val dog: Show[Dog] = animal
val cat: Show[Cat] = animal
状況
-
trait F[-A]
の宣言によってF
が生み出す型のサブタイプの関係性が逆転する - 具体例でいうと「
Dog
はAnimal
のサブタイプ =>Show[Dog]
はShow[Animal]
のスーパータイプ」
使用されるケース
- 共変の逆でinputの型として使うのが便利なケース
イメージが難しいが以下の記事が使うケースとしてイメージしやすかった
Scalaの変位指定をすると、何が嬉しいのか。反変編 - Re.Ra.Ku tech blog
終わりに
基本はメモ程度の内容なので、大したこと書いてないですがとりあえず、忘れたときにでも見返して思い出す程度のことが書かれてはいる
反変は特にイメージ難しいので忘れたらまた見返したい