Scalaの型についてまとめてみた(その3)

タム

2024.01.24

3

こんにちは。タムです。

今回はScalaの型シリーズの3回目です。

今回は上限境界について考えてみます。

上限境界

型パラメータは基本的にどんな型が入ってくるかわからないというか、

どんな型でも対応できる柔軟性を持たせるのが目的でした。

しかし、場合によっては型にある程度制約を持たせたい場合があります。

[T <: Type]という型パラメータの指定をすると、TはTypeのサブ型であるという制約が課されます。

例を示します。

@main def main() =
  val zoo = Zoo(List(Dog(), Cat()))
  zoo.sound()
  val dogZoo = Zoo(List(Dog(), Dog()))
  dogZoo.sound()
  dogZoo.animals.foreach(_.run())

class Zoo[T <: Animal](val animals: List[T]):
  def sound(): Unit =
    animals.foreach(_.cry())

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")


上記で、Zoo[T <: Animal]と定義されているので、Zooの型パラメータはAnimalもしくはそのサブ型であることが要求されます。

このように定義することで、メソッド定義内でAnimal型のメソッドcry()を使用することができます。

main関数の1行目の定数は、Zoo[Animal]型です。

一方、3行目の定数はZoo[Dog]型です。

個人的にはここがポイントだと思っていて、あくまでも型パラメータの型が可変になることで、

5行目ではDog型のメソッドであるrun()を呼び出すことが可能になっています。

class Zoo(val animals: List[Animal]):
  def sound(): Unit =
    animals.foreach(_.cry())


上記のように定義すると、上のdogZoo.animalsList[Animal]になってしまうため、run()を呼び出すことができなくなってしまいます。

いわばDog型であるという情報を失ってしまうことになります。


上限境界を用いることで、型安全性と柔軟性を兼ね備えた実装が可能になることがわかりました。

この記事をシェアする