Scala 专题教程-参数化类型(2): 信息隐藏

jerry Scala 2015年11月25日 收藏

上篇的Queue的实现在效率上来说还是比较高效的,但是却给类的使用者暴露一些不必要的实现细节,比如Queue的构造函数使用了两个List对象,其中一个还是个倒序的列表。本篇介绍如何隐藏这些不必要的信息。
在Java中,你可以使用私有的构造函数来隐藏构造函数,在Scala中的主构造器缺省包含在类定义中,但你还是可以使用private来修改其访问属性。
例如:

  1. class Queue[T] private(
  2. private val leading:List[T],
  3. private val trailing:List[T]
  4. )

在类名和参数之间的private修饰符表明该构造器是私有的,只能在类或其伙伴对象之中使用。而类本身还是可以公开访问的:

  1. scala> new Queue(List(1,2),List(3))
  2. <console>:9: error: constructor Queue in class Queue cannot be accessed in object $iw
  3. new Queue(List(1,2),List(3))
  4. ^
  5.  

由于主构造器变成私有的,因此我们需要另外的方式来构造Queue的实例,一种方法是使用辅助构造器,比如如下的辅助构造函数:

  1. def this() =this(Nil,Nil)
  2.  
  3. def this(elem: T*) = this (elems.toList,Nil)

注意,参数类 T* 代表的不定长参数类型。

另外一种方法是使用Factory 方法来构造一个队列。 一个比较简洁的方法是使用伙伴对象 ,并定义apply方法,比如:

  1. object Queue {
  2. def apply[T](xs: T*) = new Queue[T](xs.toList,Nil)
  3. }

我们使用apply定义了构造Queue的factory方法,因此调用时可以使用Queue(1,2,3)的形式来构造一个实例。 Queue(1,2,3)实际为Queue.apply(1,2,3)调用,对调用者来说看起来好像定义了一个可以全局访问的factory构造方法。而其实对于Scala来说,所有的方法都必须包含着某个类或对象中。

除了使用私有方法来隐含实现细节外,还有一种方法可以实现信息的隐藏。我们可以使用trait定义类的接口,而把实现细节全部隐藏起来(使用私有类),代码实现如下:

  1. trait Queue[T]{
  2. def head: T
  3. def tail: Queue[T]
  4. def enqueue(x:T): Queue[T]
  5. }
  6. object Queue {
  7. def apply[T](xs: T*):Queue[T] = new QueueImpl[T](xs.toList, Nil)
  8.  
  9. private class QueueImpl[T](
  10. private val leading: List[T],
  11. private val trailing: List[T]
  12. ) extends Queue[T]{
  13. private def mirror =
  14. if (leading.isEmpty)
  15. new QueueImpl(trailing.reverse, Nil)
  16. else
  17. this
  18.  
  19. def head = mirror.leading.head
  20.  
  21. def tail = {
  22. val q = mirror
  23. new QueueImpl(q.leading.tail, q.trailing)
  24. }
  25.  
  26. def enqueue(x: T) =
  27. new QueueImpl(leading, x :: trailing)
  28. }
  29. }
  30.  

这个实现定义一个Public的Trait方法,而隐藏了所有的实现细节。