《快学Scala》——对象、包
单例对象
Scala没有静态方法或静态字段,可以用object
来达到同样目的。
object Accounts {
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1; lastNumber }
}
对象本质上可以拥有类的所有特性,只有一个例外:不能提供构造器参数。
伴生对象
在Scala中,可以通过定义和类同名的(伴生)对象来提供其他语言(例如Java)中既有实例方法又有静态方法的类。类和它的伴生类可以互相访问私有特性,但必须定义在同一个源文件中。
class Accounts {
val id = Account.newUniqueNumber()
private var blance = 0.0
def deposit(amount: Double) {
blance += amount
}
...
}
object Accounts { // 伴生对象
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1; lastNumber }
}
注: object
可以扩展类以及一个或多个特质,结果是一个扩展了指定类以及特质的类的对象。
我们通常会定义和使用对象的apply
方法。当遇到如下形式的表达式时,apply
方法就会被调用:
Object(参数1,参数2,...,参数n)
通常,这样一个apply
方法返回的是伴生类的对象。示例如下:
class Account private (val id: Int, initialBalance: Double) {
private var balance = initialBalance
...
}
object Account { // 伴生对象
def apply(initialBalance: Double) =
new Account(newUniqueNumber(), initialBalance)
}
枚举
Scala并没有枚举类型,但是可以通过标准类库的Enumeration
助手类,产生枚举。定义一个扩展Enumeration
类的对象并以Value
方法调用初始化枚举中的所有可选值。
object TrafficLightColor extends Enumeration {
val Red = Value(0, "Stop")
val Yellow = Value(10) // 缺省名称为字段名
val Green = Value("Go") // ID不指定时, 将在前一个枚举值基础上加一, 从零开始
// 也可以简写为: val Red, Yellow, Green = Value
}
上例中,枚举的类型是TrafficLightColor.Value
而不是TrafficLightColor
。枚举值的ID
可以通过id
方法返回,名称可以通过toString
方法返回。
// values方法获得所有枚举值得集
for (c <- TrafficLightColor.values) println(c.id + ": " + c)
// 通过枚举的ID或名称来进行查找定位
println(TrafficLightColor(2))
println(TrafficLightColor.withName("Red"))
包
同样的类名出现在不同的包中是不冲突的。在使用这样的类的时候可以使用完全限定的名称(包名.类名),也可以使用引入语句(import
)。Scala的包和其他作用域一样地支持嵌套。通过下面形式的定义,可以访问上层作用域中的名称。
package com {
package horstmann {
object Utils {
def percentOf(value: Double, rate: Double) = value * rate /100
...
}
package impatient {
class Employee {
....
def giveRaise(rate: scala.Double) {
salary += Utils.percentOf(salary, rate) // 因为Utils类定义在父包中,所有父包中的内容都是在作用域内的,因此没必要在前面加包名
}
}
}
}
}
串联式包语句,限定了可见的成员。
package com.horstmann.impatient {
// com和com.horstmann的成员在这里不可见
package people {
class Person
...
}
}
文件顶部标记法,可以在文件顶部使用package
语句,不带花括号。这样做的话,表示文件中的所有代码均属于同一个包下。
package com.horstmann.impatient
// com和com.horstmann的成员在这里不可见
package people
class Person {
...
}
包对象
可以将工具类或者常量添加到包对象中,用package object
定义包对象:
package com.horstmann.impatient
package object people {
val defaultName = "test"
}
package people {
class Person {
var name = defaultName // 从包对象拿到的常量
...
}
}
注: defaultName不需要加限定词,因为它位于同一个包内。在其他地方,这个常量可以用com.horstmann.impatient.people.defaultName
。
包可见性
在Java中,没有被声明为public
、private
或protected
的类成员在包含该类的包中可见。在Scala中,可以通过修饰符达到同样的效果。
package com.horstmann.impatient.people
class Person {
// 在包impatient中可见
private[impatient] def desc = "A person with name " + name
}
引入
import java.awt.Color
// 引入包下所有成员
import java.awt._
// 引入类或对象的所有成员
import java.awt.Color._
在Scala中,import
语句可以出现在任何地方,并不局限在文件顶部。import
语句的效果一直延伸到包含该语句的代码块的末尾。
import java.awt.{Color, Font} // 引入包中多个成员时,可以使用选取器(selector)
import java.util.{HashMap => JavaHashMap} // 还可以重命名选到的成员
// 选取器HashMap => _将隐藏某个成员,这样HashMap无二义地指向scala.collection.mutable.HashMap
import java.util.{HashMap => _, _}
import scala.collection.mutable._
每个scala程序都隐式地以如下代码开始,这里的引入,允许覆盖之前的引入(例如scala.StringBuilder
会覆盖java.lang.StringBuilder
而不是与之冲突)。由于scala包默认被引入,对于那些以scala开头的包,可以省略scala。
import java.lang._
import scala._
import Predef._