目次
タム
2024.01.25
18
こんにちは。タムです。
今回はScalaの型シリーズの4回目です。
今回は下限境界について触れてみます。
下限境界自体はそんなに難しくないのですが、どんな例を示すかで悩みました。
というのも、ネットに出回っている他の記事を眺めていても、
大体がListのようなデータ構造を定義しているだけで、
「だったら普通にListを使えば良くない?下限境界なんて実際使うことなんてある?」
という疑問に答えることができないと思ったためです。
色々考えた結果、前回の例で使用したZooクラスを拡張するのが良さそうだと判断しました。
前回のZooクラスで、インスタンス化した後にappend
メソッドで別の動物を追加できるようにしたいとします。
ただし、現状animals
はListなので、後から要素を追加することはできません。
そこで同じオブジェクトに要素を追加するのではなく、要素が追加された新しいオブジェクトを生成するようにします。
@main def main() =
val zoo = Zoo(List(Dog(), Cat()))
zoo.sound()
val dogZoo = Zoo(List(Dog(), Dog()))
dogZoo.sound()
dogZoo.animals.foreach(_.run())
val animalZoo = dogZoo.append(Cat()) // コンパイルエラー
class Zoo[T <: Animal](val animals: List[T]):
def sound(): Unit =
animals.foreach(_.cry())
def append(animal: T) =
Zoo(animal :: animals)
class Animal:
def cry(): Unit =
println("crying")
class Dog extends Animal:
override def cry(): Unit =
println("bow wow!")
def run(): Unit =
println("running!")
class Cat extends Animal:
override def cry(): Unit =
println("meow")
def walk(): Unit =
println("walking")
上記のmain関数は6行目でコンパイルエラーを発生します。
これは、dogZoo
の型が3行目でZoo[Dog]
に固定されてしまっているためです。
appendの引数の型パラメータがTになっており、dogZoo
の場合はTがDogになるためです。
しかし、Catを入れた場合はZoo[Animal]
を生成してほしいです。
このような場合に、下限境界を用いることで、Zooに柔軟性を持たせることができます。
下限境界は、[T >: U]
と指定します(Uが下限)
@main def main() =
val zoo = Zoo(List(Dog(), Cat()))
zoo.sound()
val dogZoo = Zoo(List(Dog(), Dog()))
dogZoo.sound()
dogZoo.animals.foreach(_.run())
val animalZoo = dogZoo.append(Cat())
class Zoo[T <: Animal](val animals: List[T]):
def sound(): Unit =
animals.foreach(_.cry())
def append[U >: T <: Animal](animal: U) =
Zoo(animal :: animals)
上記のバージョンではコンパイルエラーは起こらず、期待通りanimalZoo
の型はZoo[Animal]
になります。
(なお、下限境界の設定のみではUはZooの型パラメータが満たすべき上限境界を満たしていないためZooのインスタンス化のところでコンパイルエラーになります。
そのため下限境界だけでなく上限境界も同時に設定しています。)
Zooの属性animals
の型をAnimalのサブクラスに限定しながら、後から別のAnimalを追加した際にも
型の柔軟性が保たれるような実装になりました。