上界(Upper bound)、下界(Lower bound)、視界(View bound)


在定義型態參數時,可以定義可參數化型態的上界(Upper bound)與下界(Low bound)。例如:
class Animal
class Human extends Animal
class Toy

class Duck[T <: Animal]

val ad = new Duck[Animal]
val hd = new Duck[Human]
val td = new Duck[Toy] // 編譯錯誤,不符型態邊界

在上例中,使用 <: 定義型態參數化時,必須是Animal的子類(也就是以Animal為上界),你可以使用Animal與Human參數化型態,但不可以使用Toy參數化型態,因為Toy的上界不是Animal(也就是非Animal的子類)。

一個實際應用可以用 快速排序法 中的例子來說明:
def quick(list: List[Int]): List[Int] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}

在這個例子中,是以Int來示範演算法的實作,如果要讓這個函式更一般化,可以排序任何型態的物件,則該型態必須具備 < 方法,一個方式是要求要排序的物件具有 scala.Ordered[A] 特徵。所以可以修改函式定義如下:
def quick[T <: Ordered[T]](list: List[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}

如果你定義了一個類別,具有scala.Ordered[A]特徵,則可以使用這個quick()函式來進行排序,例如:
class Ball(val radius: Int) extends Ordered[Ball] {
def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3
def compare(that: Ball) = this.radius - that.radius
override def equals(a: Any) = a match {
case that: Ball => this.radius == that.radius;
case _ => false
}
override def hashCode = 41 * radius
override def toString = "Ball(" + radius + ")"
}

val bs = List(new Ball(3), new Ball(2), new Ball(4), new Ball(1))
quick(bs).foreach(println)

你可以使用 <: 來定義上界,也可以使用 >: 來定義下界。例如:
class Alien
class Kryptonian extends Alien

class Earthman
class Spiderman extends Earthman

class Superman[T >: Kryptonian]

val s1 = new Superman[Kryptonian]
val s2 = new Superman[Alien]
val s3 = new Superman[Spiderman] // 編譯錯誤,不符型態邊界

在這邊先附帶一提的是,如果你試圖在上面所定義quick()函式上使用Int、Double、String等型態,會發生編譯錯誤:
def quick[T <: Ordered[T]](list: List[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}

val list = List(3, 1, 2, 4)
quick(list).foreach(println)

以上例而言,是因為Int並沒有scala.Ordered [A]特徵。你應該記得,當試圖使用如 1 < 2 這樣的比較時,會透過隱式轉換(Implicit conversion)將Int的1轉換為RichInt來包裹1,然後使用其<方法來取得比較結果。如果你想要上例也可以作用於具隱式轉換函式的 型態,則可以改寫為以下:
def quick[T <% Ordered[T]](list: List[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}

val list1 = List(3, 1, 2, 4)
quick(list1).foreach(println)
val list2 = List("xyz", "xab", "abc", "cde")
quick(list2).foreach(println)

注意!粗體字的部份中將原先的<:改為<%,<:是規範上界,>:是規範下界,<%其實規範視界(View bound)。原先使用 T <: Ordered[T] 的quick(),其型態參數的意義是:「T 可以是任何Ordered[T]的子型態!」,而使用 T<% Ordered[T] 的quick(),其型態參數讀法是:「T 可以是任何可被視為Ordered[T]型態的物件!」所以 T<% Ordered[T] 可以表示的型態更廣,除了可以是Ordered[T]的子型態之外,還可以是可隱式轉換至Ordered[T]的型態。關於隱式轉換,之後還會詳加介紹。