1.单例对象
scala没有静态方法或静态字段,但我们可以用object这个语法结构来达到同样目的。对象定义了某个类的单个实例,包含了我们想要的特性。如:
object Accounts {
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1;lastNumber}
}
当想要一个新账号时,调用Account.newUniqueNumber()就好。
对象的构造器在该对象第一次被使用时调用,上例中,Account的构造器在Account.newUniqueNumber()首次调用时被执行了,如果一个对象从未被使用,那构造器也不会执行。
对象本质上可以拥有类的所有特性,甚至可以扩展其他类或特质,但唯一不同的是:我们不能提供构造器参数。
在Java中使用单例对象的场景,在scala中我们都可以用对象来实现:
1.作为存放工具函数或常量的地方
2.高效地共享单个不可变实例
3.需要用单个实例来协调某个服务时
2.伴生对象
在Java中,我们通常会用到既有实例方法又有静态方法的类。在scala中我们可以通过类和类同名的伴生对象来达到同样的目的。如:
class Account {
val id = Account.newUniqueNumber()
private var balance = 0.0
def deposit(amount: Double) {balance += amount}
...
}
object Account { //伴生对象
private var lastNumber = 0
private def newUniqueNumber() = {
lastNumer += 1
lastNumber
}
}
类和它的伴生对象可以相互访问私有属性,但它们必须存在于一个源文件中。
3.apply方法
当遇到如下的表达式时,apply方法就会被调用:
Object(参数1,参数2,…,参数N)
通常,这样的一个apply方法返回的是伴生类的对象。
for ex:
Array对象定义了apply方法,使我们可以用下面的表达式来创建数组:
Array(“Mary”,“Jack”,“Rose”,“Jkson”)
为什么不用构造器呢?因为对于嵌套而言,省去new会方便很多,如:
Arrray(Array(1,7),Array(2,9))
需要注意的是,Array(100)和new Array(100)很容易弄混,前一个是apply(100),输出单元素100的Array[Int],而第二个表达式调用的是构造器this(100),结果为Array[Nothing],包含100个null元素。
实例:
class Account private (val id: Int,initialBalance: Double) {
private var balance = initialBalance
...
}
object Account { //伴生对象
def apply(initialBalance: Double) = new Account(newUniqueNumber(),initialBalance)
...
}
这样你就可以这样构造账号对象:
val acct = Account(1,1000.0)
4.应用程序对象
每个scala程序必须从一个对象的main方法开始,这个方法类型为Array[String] => Unit:
object Hello {
def main(args: Array[String]){
println("Hello Scala")
}
}
除了自己提供main方法外,我们也可以扩展App特质,将main方法放入构造器方法内:
object Hello extends App {
println("Hello Scala")
}
如果需要命令行参数,可以通过args属性得到:
object Hello extends App {
if (args.length > 0)
println(args(0))
else
println("Hello Scala")
}
5.枚举
scala没有枚举类型。不过标准类库提供了一个Enumeration助手类,可以用于产出枚举。
定义一个继承Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。如:
object TrafficLightColor extends Enumeration {
val Red, Yellow, Green = Value
}
这里我们定义了三个字段,Red,Yellow和Green,然后用Value调用将它们初始化,
val Red = Value
val Yellow = Value
val Green = Value
每次调用Value方法都会返回内部类的新实例,该内部类也叫做Value,或者可以向Value方法中
传入id,名称:
val Red = Value(0,"Stop")
val Yellow = Value(11) //名称为“Yellow”
val Green = Value("") //id为12
如果不指定,那么id会将前一个枚举值基础上加1,从零开始,缺省名称为字段名。
定义完成后就可以用TrafficLightColor.Red这样来引用枚举值了,想简便一些可以:
import TrafficLightColor._
直接引入枚举值。
需要注意的是,枚举的类型是TrafficLightColor.Value,而不是TrafficLightColor,这个是握有这些值的对象。有人
推荐可以增加一个类型别名:
object TrafficLightColor extends Enumeration {
type TrafficLightColor = Value
val Red, Yellow, Green = Value
}
现在枚举的类型变为了TrafficLightColor.TrafficLightColor,但仅当我们使用import语句这样做才有意义
如:
import TrafficLightClor._
def doWhar(color: TrafficLight) = {
if (color == Red) "Stop"
else if (color == Yellow) "hurry up"
else "go"
}
枚举值的id可以通过id方法返回,名称通过toString方法返回。
对TrafficLightColor.values的调用输出所有枚举值的集合:
for (x <- TrafficLightColor.values) println(x.id + ":" + x.toString)
最后,可以通过枚举的id或名称来进行查找定位,下面两句都输出为TrafficLightColor.Red对象:
TrafficLightColor(0) //将调用Enumration.apply方法
TrafficLightColor.withName("Red")