何となく分かった気になる共変・反変

Scalaを勉強していて出てきた言葉、共変と反変。
共変は直感的に理解しやすかったですが、反変は最初よく分かりませんでした。
それが、以下の様な例えを考えたら何となく分かってきました。

はじめに、型パラメータとなるクラス

こんな2つのクラスがあるとします。

class Food

class Sushi extends Food

これらのクラスを型パラメータとするクラスを、これから考えていきます。

わりと理解しやすい共変

共変の例として、こんなクラスがあるとします。

class Restaurant[+T] {
  def serve(): T = // 何かしらT型を返す実装...
}

そうすると、こんな変数を定義できます。

val familyRestaurant: Restaurant[Food] = ...
// familyRestaurant.serve()はFoodを返す。

val sushiya: Restaurant[Sushi] = ...
// sushiya.serve()はSushiを返す。

型パラメータTが共変ということは、Restraurant[Food]型の変数にRestaurant[Sushi]型の変数を代入できる、つまり以下の様に考えられます。

val restaurant1: Restaurant[Food] = familyRestaurant
// とりあえずこれはOK。restaurant1.serve()はFoodを返す。

val restaurant2: Restaurant[Food] = sushiya
// 型パラメータが共変だからこれもOK! restaurant2.serve()はFoodを返す。
// restaurant2の実体は寿司屋だけど、寿司屋が返すSushiはFoodの一種だから問題なし。

val restaurant3: Restaurant[Sushi] = familyRestaurant
// これはコンパイルエラー。restaurant3.serve()はSushiを返さないといけない。
// だけどrestaurant3の実体はファミレス。
// ファミレスが返すのは大まかな括りであるFoodであり、Sushiとは限らないのでコンパイルエラー。

こんな風に考えて、共変が分かった気になりました。
共変は直感的にも理解しやすいですよね。

そして反変

いよいよ小難しそうな反変を考えてみます。
例として、こんなクラスがあるとします。

class Eater[-T] {
  def eat(menu: T) = // T型の引数を受け取ってモグモグする実装...
}

そうすると、さっきと同じようにこんな変数を定義できます。

val kuishinbo: Eater[Food]
// 食いしん坊は何でも食べられる。
// kuishinbo.eat(new Food)とすることもできるし、
// kuishinbo.eat(new Sushi)でもよい。

val edokko: Eater[Sushi]
// 江戸っ子は寿司しか食べない。
// edokko.eat(new Sushi)はいいけど、
// edokko.eat(new Food)はコンパイルエラーになってしまう。

型パラメータTが反変ということはえーと、[...]の中身の関係が共変と逆だから、Eater[Sushi]型の変数にEater[Food]型の変数を代入できる、つまり以下の様に考えられます。

val customer1: Eater[Food] = kuishinbo
// とりあえずこれはOK。customer1.eat()はFoodを受け取れる。

val customer2: Eater[Sushi] = kuishinbo
// 型パラメータが反変だからこれもOK! customer2.eat()はSushiを受け取れないといけない。
// customer2の実体は食いしん坊だから、喜んでSushiを食べられる。

val customer3: Eater[Food] = edokko
// これはコンパイルエラー。 customer3.eat()はFoodを受け取れないといけない。
// だけどcustomer3の実体は江戸っ子。Sushiならいいけど、Food全般となると無理。

いやー、これで反変も分かった気になりました。

さらっと流しましたが、共変の型パラメータの使い所はメソッドの返り値(例でいうとserve())、反変の型パラメータの使い所はメソッドの引数(例でいうとeat())、というのが基本になります。

カテゴリ:Default 時間:2017-09-22 人気:0
この記事では、 Scala

関連記事

Copyright (C) socapnw.com, All Rights Reserved.

Socapnw All Rights Reserved.

processed in 0.205 (s). 9 q(s)