scala 上限境界・共変

上限境界の理解ができていなかったので、理解を深めるためのメモ。


`<:`で定義できる。
上限に指定した型のサブ型しか受け取ることができなくする。

## 例1

PersonOrderオブジェクトのorderMergeSortメソッドには、上限境界が指定されている。
`[T <: Ordered[T]]` と指定されているので、TにはOrderedのサブ型がくる
なので`orderMergeSort`の引数`List[T]`は、`Person`(`Ordered[T]`を継承した型)のみ受け取れる。


```scala

// PersonクラスにOrderedトレイトを継承されている
class Person(val firstName: String, val lastName: String) extends Ordered[Person] {
  override def compare(that: Person) = {
    val lastNameComparison: Int = lastName.compareToIgnoreCase(that.lastName)
    if(lastNameComparison != 0)
      lastNameComparison
    else
      firstName.compareToIgnoreCase(that.firstName)
  }

  override def toString: String = firstName + " " + lastName

}

object PersonOrder {
  val people = List(new Person("tomoya", "kinsho"), new Person("nana", "kinsho"))

  //　上限境界が指定されているので、TにはOrderedを継承した型（Ordered[T]のサブクラス）しか指定できない
  // なので、引数には、Ordered[T]のサブクラスのListしか受け取れない
  // 今回は、List[Person]
  def orderMergeSort[T <: Ordered[T]](xs: List[T]): List[T] = {
    ???
  }
}
```


## 例2


抽象クラスAnimalの抽象型に`SuitableFood <: Food`と上限境界を設けることで、Animalを継承した具象クラスでは
Food型のサブ型のみ指定できる。
このようにすることで、Cow(牛)がFish（魚）を食べる、などと言う奇妙はことを制御できる。

```scala
class Food
class Grass extends Food
class Fish extends Food

abstract class Animal {
  // SuitableFoodをFoodの上限境界に指定しておいて、Animalのサブクラス内でそれぞれ型を指定させる
  // その際に、上限境界の制限が付けられる（Foodのサブクラスのみ指定できる）
  type SuitableFood <: Food
  def eat(food: SuitableFood): Unit
}

class Cow extends Animal {
  // GrassはFoodのサブクラスなので指定できる
  override type SuitableFood = Grass
  override def eat(food: Grass): Unit = {}
}

val animal = new Cow

animal.eat(new Grass) // OK
animal.eat(new Fish) // コンパイルエラー. Grass型のみ渡せる

```


## 例3




```scala

trait StringBase {
  val name: String
}

case class Keyword(override val name: String) extends StringBase

case class Location(override val name: String) extends StringBase

case class Price(override val name: String) extends StringBase

class GymUseCase @Inject()(gymRepository: GymRepository){

  def search(request: SearchRequest) = {
    MysqlSearchRequest.of(request).map(gymRepository.search) match {
      case Some(res) => res
      case None => Future.successful(0L)
    }
  }
}

case class MysqlSearchRequest[+T](column: String, value: T)

object MysqlSearchRequest {
  // MysqlSearchRequest[StringBase]という戻り型を返さないといけないが、
  // MysqlSearchRequest[+T]としていることで、
  // MysqlSearchRequest[DomainKeyword]、MysqlSearchRequest[DomainPrice]、MysqlSearchRequest[DomainLocation]
  // というStringBaseのサブクラスも代入できる！！！！
  // +Tを指定しない場合は、このメソッドの返り型は、厳格にMysqlSearchRequest[StringBase]のみになる
  def of(request: SearchRequest): Option[MysqlSearchRequest[StringBase]] = {
    request.searchOptionType match {
      case Keyword => {
        request.keyword.map(MysqlSearchRequest[DomainKeyword]("keyword", _))
      }
      case Price =>{
        request.price.map(MysqlSearchRequest[DomainPrice]("price", _))
      }
      case Location =>{
        request.location.map(MysqlSearchRequest[DomainLocation]("location", _))
      }
    }
  }
}

@ImplementedBy(classOf[GymRepositoryImple])
trait GymRepository {
  // 上限境界を指定することで、MysqlSearchRequestの型パラメータには、StringBaseのサブクラスしか指定できないようになる
  // つまり、TにはStringBase, Keyword, Location, Priceしか渡せない
  // さらに、この上限境界をつけることで、StringBaseのフィールド値にアクセスできる
  def search[T <: StringBase](request: MysqlSearchRequest[T]): Future[String]
}

```