LoginSignup
1
2

型の共変 と 反変 をGenericsなしで説明する

Posted at

Genericsなしで 共変 と 反変 を説明している動画を見つけて、おもしろかったので投下します。


サンプルコード

interface VendingMachine {
	fun purchase(money: Coin): Snack
}

sealed interface Pruduct 

sealed interface Snack: Pruduct {
	data object Candy: Snack
	data object Cookie: Snack
}

sealed interface Money

sealed interface Coin: Money {
	data object Quarter: Coin
	data object Dime: Coin
}

用語

VendingMachineは、 CoinとSnackというパラメータを持っている と言える。

VendingMachineはコンテナ というメンタルモデルで表現できる。

型の共変

外に出ていくパラメータとコンテナの関係

外に出ていくパラメータ

  • コンテナのプロパティ
  • 関数の戻り値

そのinterface によって “生成される” パラメータ とも言える。

コンテナを具体化する時に生成されるパラメータの型を具体化することは 型安全である

class CandyBarVendingMachine: VendingMachine {
    override fun purchase(coin: Coin): Snack.Candy = randomCandy(coin)
}

なぜなら、Snack であっても Candyを安全にインスタンス化して返せるから

class CandyBarVendingMachine: VendingMachine {
    override fun purchase(coin: Coin): Snack = randomCandy(coin)
}

コンテナが具体的になるにつれて、外に出ていくパラメータも具体的にできる性質のことをCovariance(共変) という。

型の反変

内側で消費、操作 されるパラメータとコンテナの関係

内側で消費、操作されるパラメータ

  • 関数の引数

その interface によって “操作”,”消費” される パラメータとも言える

コンテナを具体化する時に、“操作”,”消費” される パラメータの型をより一般的にすることは 型安全である

class CandyBarVendingMachine: VendingMachine {
    override fun purchase(money: Money): Snack.Candy = randomCandy()
}

なぜなら、MoneyにはCoinも含まれるため、操作数側からするとCoinとしても使えるから。

class CandyBarVendingMachine: VendingMachine {
    override fun purchase(money: Money): Snack.Candy  {
	    when (money) {
		    is Coin -> randomCandy(coin)
		    ..
	    }
    }
}

ただし、KotlnではこれはCompilerに拒否される。Kotlinでは関数のオーバーロードが許可されているのでそちらと見分けがつかないため。

'purchase' overrides nothing

ので、型パラメータ T を使わない場合は以下の方法を取る必要はある

// override とoverload を分離
class CandyBarVendingMachine: VendingMachine {
    fun purchase(money: Money): Snack.Candy = TODO()
    override fun purchase(coin: Coin): Snack =  randomCandy(coin as Money)
}
// or
// 変数にする
interface VendingMachine {
	val purchase: (Coin) -> Snack
}
class CandyBarVendingMachine: VendingMachine {
	override val purchase: (Money) -> Snack = { randomCandy(it) }
}

CandyBarVendingMachineVendingMachine のサブタイプで、MoneyはCoinのスーパータイプ 。つまり、コンテナが具体的になる際に、 “操作”,”消費” される パラメータ は 一般的になることができる。これを **Contravariance( 反変)**という。

ジェネリクス を使うと

interface VendingMachine<in T, out R> {
	fun purchase(money: T): R
}

in が Contravariance を意味していて、Tは より一般的な型になれる。

out が Covarianceを意味していて、Rは より具体的な方になれる。

VendingMachine<Coin, Snack>
↑↓
VendingMachine<Money, Candy>
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2