1.4 如果你来自某种动态语言
现在已经很难找到没玩儿过或没听说过Ruby、Groovy、Python这类语言的开发人员了。 动态语言阵营对静态类型语言最大的不满主要是效率问题,编程者不得不编写很多无聊且影响效率的样板代码。 确实,与Java相比,动态类型语言的诸如闭包和语言的可扩展性等优点举不胜举。 但Scala却有所不同。
在对比静态和动态类型语言之前,让我们先了解下Scala对闭包(closure)和混入(mixin)的支持。 下面程序清单展示了如何用Ruby统计一个给定文件的行数。
清单1.6 使用Ruby统计文件行数
count = 0File.open "someFile.txt" do |file|file.each { |line| count += 1 }end
例中你打开了一个someFile.txt文件,每新读一行,count加1。
小意思!下面的代码清单展示了Scala的做法。
清单1.7 使用Scala统计文件行数
val src = scala.io.Source.fromFile(“someFile.txt”)val count = src.getLines().map(x => 1).sum
两段代码看起来非常相似。
其实在Scala中,有很多种方法都可以达到同样效果,上面例子是使用的map方法,对每一行都返回一个1,然后使用sum方法来计算总共的行数。
Scala使用特质(trait)来支持混入,特质类似于带有部分实现的抽象类。
例如,你可以创建一个集合类型,通过混入Scala的Iterable特质,可以让使用者以迭代器(iterable)的方式访问文件。
这个集合类型只实现一个iterator方法:
class FileAsIterable {def iterator = scala.io.Source.fromFile("someFile.txt").getLines()}
此时,如果你混入Scala提供的Iterable特质,这个新建的FileAsIterable类会变成Iterable类型,支持Iterable所有的方法:
val newIterator = new FileAsIterable with Iterable[String]newIterator.foreach { line => println(line) }
在这个例子中,你使用了Iterable特质的foreach方法,对文件中的每一行进行打印。
Scala的2.10版本添加了对动态类型(Dynamic type)的支持。利用这一特性,你可以在运行时动态的给一个类型添加方法和属性。这与Ruby的method_missing特性非常相似,如果你正在构建一个领域特定语言(domain-specific language, DSL),这是非常有用的。例如,Scala的map是一个键值对的集合,如果你想通过一个给定的键来访问它的值,你可以像如下这样做:
val someMap = Map("foo" -> 1, "bar" -> 2)someMap.get("foo")
someMap是一个包含两个键值对的集合,someMap.get("foo")会返回1。而使用Dynamic的话,我们可以直接访问键,就像它们是类型的一部分一样:
class MyMap extends Dynamic {...def selectDynamic(fieldName: String) = map.get(fieldName)private val map = Map("foo" -> "1", "bar" -> 2)}val someMap = new MyMapsomeMap.foosomeMap.bar
例中的魔法师是selectDynamic方法(Scala中方法的定义使用关键字def)。
当Scala编译器检测到foo不是类型的一部分时,并不马上放弃,而是继续判断它是不是Dynamic的子类,如果是,则编译器会寻找selectDynamic方法并对它进行调用,如果这个方法没有被提供,将会产生一个编译错误。
Scala也支持隐式变换(implicit conversion)。 这一特性与Ruby的open classes非常类似,但是额外提供了作用域(scoped)和编译时检测。 隐式变换的例子将贯穿本书。
链接
- 目录
- 下一节: 1.4.1 静态类型才是正确的道路
