1.4 如果你来自某种动态语言

现在已经很难找到没玩儿过或没听说过Ruby、Groovy、Python这类语言的开发人员了。 动态语言阵营对静态类型语言最大的不满主要是效率问题,编程者不得不编写很多无聊且影响效率的样板代码。 确实,与Java相比,动态类型语言的诸如闭包和语言的可扩展性等优点举不胜举。 但Scala却有所不同。

在对比静态和动态类型语言之前,让我们先了解下Scala对闭包(closure)和混入(mixin)的支持。 下面程序清单展示了如何用Ruby统计一个给定文件的行数。

清单1.6 使用Ruby统计文件行数

  1. count = 0
  2. File.open "someFile.txt" do |file|
  3. file.each { |line| count += 1 }
  4. end

例中你打开了一个someFile.txt文件,每新读一行,count加1。

小意思!下面的代码清单展示了Scala的做法。

清单1.7 使用Scala统计文件行数

  1. val src = scala.io.Source.fromFile(“someFile.txt”)
  2. val count = src.getLines().map(x => 1).sum

两段代码看起来非常相似。 其实在Scala中,有很多种方法都可以达到同样效果,上面例子是使用的map方法,对每一行都返回一个1,然后使用sum方法来计算总共的行数。

Scala使用特质(trait)来支持混入,特质类似于带有部分实现的抽象类。 例如,你可以创建一个集合类型,通过混入Scala的Iterable特质,可以让使用者以迭代器(iterable)的方式访问文件。 这个集合类型只实现一个iterator方法:

  1. class FileAsIterable {
  2. def iterator = scala.io.Source.fromFile("someFile.txt").getLines()
  3. }

此时,如果你混入Scala提供的Iterable特质,这个新建的FileAsIterable类会变成Iterable类型,支持Iterable所有的方法:

  1. val newIterator = new FileAsIterable with Iterable[String]
  2. newIterator.foreach { line => println(line) }

在这个例子中,你使用了Iterable特质的foreach方法,对文件中的每一行进行打印。

Scala的2.10版本添加了对动态类型(Dynamic type)的支持。利用这一特性,你可以在运行时动态的给一个类型添加方法和属性。这与Ruby的method_missing特性非常相似,如果你正在构建一个领域特定语言(domain-specific language, DSL),这是非常有用的。例如,Scala的map是一个键值对的集合,如果你想通过一个给定的键来访问它的值,你可以像如下这样做:

  1. val someMap = Map("foo" -> 1, "bar" -> 2)
  2. someMap.get("foo")

someMap是一个包含两个键值对的集合,someMap.get("foo")会返回1。而使用Dynamic的话,我们可以直接访问键,就像它们是类型的一部分一样:

  1. class MyMap extends Dynamic {
  2. ...
  3. def selectDynamic(fieldName: String) = map.get(fieldName)
  4. private val map = Map("foo" -> "1", "bar" -> 2)
  5. }
  6. val someMap = new MyMap
  7. someMap.foo
  8. someMap.bar

例中的魔法师是selectDynamic方法(Scala中方法的定义使用关键字def)。 当Scala编译器检测到foo不是类型的一部分时,并不马上放弃,而是继续判断它是不是Dynamic的子类,如果是,则编译器会寻找selectDynamic方法并对它进行调用,如果这个方法没有被提供,将会产生一个编译错误。

Scala也支持隐式变换(implicit conversion)。 这一特性与Ruby的open classes非常类似,但是额外提供了作用域(scoped)和编译时检测。 隐式变换的例子将贯穿本书。

链接