Scala的集合框架基于接口一致的理念对外提供相关接口。框架的设计原则是尽量避免重复(即代码重构时的要求:不重复代码,有且仅有一次的设计原则),对外提供统一的访问接口,以方便客户端的使用。设计中使用的方法是,在集合模板中实现大部分操作,然后由各个基类和具体实现子类来继承。
下面简单分析Scala集合框架的各个构造块及其支持的原则。
几乎所有的集合操作都是基于遍历器(traversals)和构造器(builders)来实现的。遍历器(traversals)由Traversable提供的foreach方法实现遍历,对应的,构建一个新的集合则是通过一个Builder实例来实现的。
下面是Builder特质的精简代码:
对应mapResult方法的简单使用的代码如下:
类库重构时,是以构建自然类型、最大限度地实现代码共享为主要设计目标的,尤其是Scala类库设计,还同时遵循了“相同结果类型”的设计原则(只要可能,集合的转换操作会返回和集合类型相同的结果)。为了实现这两点,Scala类库的设计通过在一个被称为实现特质(implementation traits,即具有实现代码的特质)上使用泛型的构造器和集合遍历器,即TraversableLike特质,该特质的精简代码如下所示:
和Java中以Impl为后缀来提供具体实现代码类似,只是在Scala使用Like作为后缀,例如,Scala的IndexedSeq混入(mix-in)了IndexedSeqLike特质的具体实现,对应代码如下所示:
在Scala类库设计时遵循了“相同结果类型”的设计原则,下面基于map转换操作给出实现“相同结果类型”的简单分析,主要从两个方面进行说明。对应的TraversableLike中Map转换操作的代码如下所示:
(1)首先从静态(对应编译期)的角度去分析
Map操作的第二个参数为CanBuildFrom类型的隐式值,并且在代码中,通过将集合自身传入该隐式值,来构造一个构造器变量b,再使用遍历的foreach方法将转换函数f作用于各个元素,并且将结果添加到构造器实例中,最终,再使用构造器的result方法,构造一个新的集合作为返回值。
其中,CanBuildFrom特质的精简代码如下所示:(www.daowen.com)
由上可知,最终返回的新集合是由对应的构造器来决定的,而构造器又是从CanBuild⁃From类型的隐式值中获取的。在Scala集合类库中,分别在不同集合中定义与集合相关的CanBuildFrom类型的隐式值,由隐式值查找的匹配规则来决定最终的隐式值。
例如List集合,在List的伴生对象中提供了如下定义的隐式值:
获取CanBuildFrom实例后,对应的,集合自身也可以得到一个构造器实例,由第四行的隐式值的返回类型可知,CanBuildFrom实例构建的Builder实例,其构建(通过result操作)的新的集合类型为List[A],符合“相同结果类型”设计原则。
(2)从动态(对应运行期)的角度去分析
以下面的实例进行分析:
当基于面向接口编程进行开发时,在实例中,xs在编译时的静态类型Iterable[Int],应该类型的隐式值的定义代码如下所示:
和前面的分析类似,可以看出CanBuildFrom的构建的结果集合的类型为Iterable[A],对应的,Builder的结果集合的类型也为Iterable[A]。但xs的实际类型为List[Int],如果此时对xs做Map操作后得到的新集合类型为Iterable[A],就破坏了“相同结果类型”的设计原则。
在Scala中,为了解决上面破坏设计原则的问题,利用了抽象类型中的动态绑定机制,由具体集合类型自身提供构建构造器的方法,即newBuilder方法。Xs在运行期的类型实际是List[Int],由于动态绑定,实际调用的是List的newBuilder方法。在List中该方法的定义如下所示:
可以看到,List的newBuilder方法中指定了List[A],也就是这里的List[Int],作为构造器的结果集合类型,因此最终也遵循了“相同结果类型”的设计原则。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。