5.6 Scala标准库
详细讨论OOP后,下面来着手编写有点用的类和方法。随Scala安装了Scala标准库,这个库很大,包含Scala特有的类。本节讨论如下主题:
- 泛型;
- 集合;
- XML处理。
5.6.1 泛型
Java使用表示法ClassName来表示支持泛型的类。本书前面说过,定义有些类(如接口Map)时,需要指定多种类型。对于Map,需要指定它将存储的键和值的类型。
在Scala中,使用表示法ClassName[T]:
val aList = List[Int]($text-1, 2, 3, 5)
这将创建一个不可修改的列表,其中包含5个元素。由于指定了List[Int],因此只能在其中添加类型为Int或可向上转换为Int的类的实例。
同样,对于需要指定两种类型的泛型类,可这样定义:
val m = Map[String, String]("key1" -> "value1", "key2" -> "value2")
这里创建了一个映射,它将键"key1"和"key2"分别映射到字符串值"value1"和"value2"。
5.6.2 集合
Scala标准库提供了很多集合API,这些API分为两大类:
- 不可修改的集合;
- 可修改的集合。
不可修改的集合通常位于scala.collection.immutable包中,而可修改的类位于scala.collection.mutable包中。默认自动导入的Scala包包含指向多个不可修改的集合类的引用,因此,如果没有显式地导入不可修改或可修改的集合类,默认使用的将是不可修改的版本。
- 不可修改的列表
在前面两个示例中,使用的分别是不可修改的列表和映射。这些数据结构将在初始化期间被填充。可使用不可修改的集合来创建新列表,下面的示例基于既有列表的内容创建新列表:
val immutableList = List[Int]($text-1, 2, 3, 4, 5)val newImmutableList1 = 0 :: immutableListval newImmutableList2 = immutableList ::: List(6, 7)
乍一看,这可能让人迷惑。第2行创建一个新的列表实例,其中包含0和另一个列表的副本,因此newInstance1为ListInt。运算符::左边是要添加到新建实例中的值,而右边是要复制的列表。由于List类针对LIFO(后进先出)操作进行了优化,因此创建新列表实例时,将新元素放在既有列表的元素前面会更容易。
然而,创建新列表时,也可在列表末尾添加新元素。为此可使用运算符:::,但这个运算符两边都必须是列表。在前面的示例中,newImmutableList2将包含如下元素:1、2、3、4、5、6、7。
请注意,创建新列表时,并没有修改原来的列表aList。
- 可修改的列表
还有可修改的列表版本,这种列表名为ListBuffer,位于scala.collection.mutable包中。完全可以想见,这种版本提供了你熟悉的方法,如append()、remove()和clear():
import scala.collection.mutableval aMutableList = mutable.ListBuffer[Int]($text-1, 2, 5)aMutableList.remove(2)aMutableList.append(3)println(aMutableList(0))println(aMutableList)
这将打印1和ListBuffer(1, 2, 3)。
在Scala中,一种最佳实践是尽可能使用运算符。下面是使用运算符实现的前一个示例:
import scala.collection.mutableval aMutableList = mutable.ListBuffer[Int]($text-1, 2, 5)aMutableList -= 5aMutableList += 3println(aMutableList)
运算符-=将指定的值从列表中删除,而+=在列表中添加新值。
注意,使用运算符
-=,必须指定要删除的值。另外,请注意将删除的元素的索引。
在Scala提供的众多集合类中,有一个名为ArrayBuffer的可修改类。这个类在内部是使用数组实现的,因此使用索引来访问其元素的效率要高得多:
import scala.collection.mutableval aMutableArray = mutable.ArrayBuffer[Int]($text-1, 2, 3)aMutableArray += 4println(aMutableArray(3))
这将打印4。与ListBuffer类一样,ArrayBuffer也实现了很多运算符。
- 不可修改的映射
Scala标准库包含众多实现各不相同的映射类,这里将讨论标准类Map,它是不可修改的:
val immutableMap = Map[Int, String](10 -> "ten", 20 -> "twenty")println(immutableMap(20))
基于既有的映射创建新的映射实例很容易,下面的示例修改了前面定义的映射:
val newImmutableMap = immutableMap + (30 -> "thirty")
要将两个映射合而为一,可使用运算符++:
val combinedMap = newImmutableMap ++ Map[Int, String](24 -> "twentyfour")
- 可修改的映射
Scala标准库中的可修改映射类之一是HashMap,它很像Java类java.lang.HashMap:
import scala.collection.mutableval mutableMap = mutable.HashMap[Int, String](10->"ten",50->"fifty")mutableMap += (100 -> "Hundred", 150 -> "Hundred and fifty")mutableMap -= 10println(mutableMap)
这将打印mutableMap.type = Map(50 -> fifty, 100 -> Hundred, 150 -> Hundred and fifty)。
注意,在前面的不可修改映射示例中演示的运算符也可用于
HashMap实例。用于HashMap实例时,这些运算符的工作原理不变,也将创建新实例,而不是修改当前HashMap实例。
5.6.3 XML处理
Scala标准库包含一个功能强大的XML库,可帮助创建和使用XML文档。这里将演示如何使用XML字面量来生成XML文档。通过使用这种功能,可直接在Scala源代码中输入XML内容。在幕后,Scala编译器将使用其XML库来填充变量,并验证生成的XML是否有效。
建议你不要直接在Sacla REPL shell中输入下面的示例,而使用你喜欢的文本编辑器创建一个源代码文件,再将其路径传递给命令
scala。直接在REPL shell中输入代码和XML时,这个交互式解析器可能出现故障。
下面是一个简单的示例,它生成一个包含XML的String:
val productCode = "PC Monitor"val qty = 2.toString()val xmlContent =<basket><line><product qty={ qty }>{ productCode }</product></line></basket>println(xmlContent)
请注意,在XML元素中使用的所有变量都必须是字符串。另外请注意,在Scala中,字面量2是一个对象,因此可对其调用方法toString()。上述程序生成的输出如下:
<basket><line><product qty="2">PC Monitor</product></line></basket>
通过创建返回XML元素的函数,可轻松地将集合添加到XML输出中。返回XML元素的函数应返回一个xml.Elem实例:
def createXMLProduct(productCode: String): xml.Elem = {<product qty="1">{ productCode }</product>}val productCodes = List[String]("Keyboard", "Mouse")def lines =<basket><products> {productCodes.map(x => createXMLProduct(x))}</products></basket>println(lines.toString())
这里没有手动遍历集合,而是调用了方法map。对于集合中的每个元素,方法map都使用传递给它的lambda函数来返回新内容,从而对列表进行转换。在这里,它将包含String的列表productCodes转换为一个包含XML元素的新列表。函数map很好地展示了下一节将更详细地讨论的函数式编程。上述脚本的输出如下:
<basket><products><product qty="1">Computer Keyboard</product><product qty="1">Mouse</product></lines></products></basket>
注意,在实际编程中,不应以硬编码的方式将数量(属性
qty)指定为1。
注意,使用运算符