通过参数化型别,我们可以将先前的 IntSet 以泛型风格重写
1: // from IntSet to Set 2: abstract class Set[A] { 3: def incl(x: A): Set[A] 4: def contains(x: A): Boolean 5: } 6: // ... But非常直接了当的做法。类似的代码在 Groovy 中将能够顺利编译并(多数情况下)正确运行,但是在 Scala 中,我们将遇到小小的问题:在 IntSet 中,我们在 incl 和 contains 方法中均调用了 Int 的 > 和 < 方法,但当使用泛型的时候,我们无法保证 A 具有这两个方法。为此,我们必须对 A 进行小小的修饰:要求 A 必须已混入了 trait Ordered。
1: // The parameter declaration A <: Ordered[A] introduces A as a type 2: // parameter which must be a subtype of Ordered[A], i.e. its values must be 3: // comparable to values of the same type. 4: trait Set[A <: Ordered[A]] { 5: def incl(x: A): Set[A] 6: def contains(x: A): Boolean 7: } 8: 9: class EmptySet[A <: Ordered[A]] extends Set[A] { 10: def contains(x: A) = false 11: def incl(x: A): Set[A] = 12: new NonEmptySet(x, new EmptySet[A], new EmptySet[A]) 13: } 14: 15: class NonEmptySet[A <: Ordered[A]](elem: A, left: Set[A], right: Set[A]) 16: extends Set[A] { 17: def contains(x: A): Boolean = 18: if(x < elem) left contains x 19: else if(x > elem) right contains x 20: else true 21: def incl(x: A): Set[A] = 22: if(x < elem) new NonEmptySet(elem, left incl x, right) 23: else if(x > elem) new NonEmptySet(elem, left, right incl x) 24: else this 25: } 26: 27: case class Num(value: Double) extends Ordered[Num] { 28: def compare(that: Num): Int = 29: if(this.value < that.value) -1 30: else if(this.value > that.value) 1 31: else 0 32: } 33: 34: val s = new EmptySet[Num].incl(Num(1.0)).incl(Num(2.0)) 35: println(s.contains(Num(1.5))) 36: // Int does not conform to type parameter bound Ordered[Int] 37: // val w = new EmptySet[Int] // Error! 问题是,如果你在设计 Num 类的时候没有考虑到 Set 的应用,那么你将不会将 Num 声明为 Ordered 的子类,从而无法在日后的场合应用以上的代码,同样的道理,你无法对 EmptySet 应用 Int、Double 或其它显而易见可能会被用到的类。原书中提供了另一中解决方案(view bounds),可惜我无法找到运行它的方法,鉴于 view bounds 将在第 15 节详细介绍,这里就先略过了。#TBD通过指定 A 是 B 的子类强迫 push 接收 A 或其基类型。不仅如此,其返回值也会根据 B 的类型自动做相应的改变!(说实话,除了这个自动改变返回值类型的特点外,我一点也无法领会 Scala 在类型处理上的美妙之处,作为强测试的拥护者,我真的真的不在意强类型,but,目前我还没有学完,不是吗?)
Nothing
作为递归的起点,空的 Stack 可以写作 class EmptyStack extends Stack[A],但是引入协变后,从逻辑上,我们只需要一个对象就可以表示空栈 1: abstract class Stack[+A] { 2: def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x, this) 3: def isEmpty: Boolean 4: def top: A 5: def pop: Stack[A] 6: } 7: 8: object EmptyStack extends Stack[Nothing] { 9: def isEmpty = true 10: def top = error("EmptyStack.top") 11: def pop = error("EmptyStack.pop") 12: } 13: 14: class NonEmptyStack[+A](elem: A, rest: Stack[A]) extends Stack[A] { 15: def isEmpty = false 16: def top = elem 17: def pop = rest 18: } 一个对象就可以了。在此,Nothing 是所有类的子类;对应的 Any则是所有类的超类。(突然想吼指环王的台词:One ring to rule them all...)