单例对象
Scala没有静态方法或静态字段,你可以用object这个语法结构来达到同样的目的。对象定义了某个类的单个实例,包含你想要的特性。如,
object Number {
private var num = 0;
def newUniqueNum() = {
num += 1
num
}
def main(args: Array[String]) {
println(Number.newUniqueNum()) //打印1
}
}
注:Scala的单例对象和Java中的静态方法/字段调用方式一致。在Scala中,对象的构造器在该对象第一次被使用时调用,若一个对象从未被使用,那么其构造器也不会被执行。对象本质上可以拥有类的所有特性-它甚至可以扩展其它类或特质,但是你不能提供构造器参数。
Scala单例对象使用场景
1.作为存放工具函数或常量的地方
2.高效共享单个不可变实例
3.需要单个实例来协调某个服务时(可参考单例模式)
伴生对象
在Java或C++中,你通常会用到即有实例方法又有静态方法的类。在Scala中,你可以通过类和与类同名的“伴生”对象来达到同样的目的。如
class Num {
private val id = Num.newUniqueNum
private def test() = {}
}
object Num {
private var num = 0
new Num().test()
private def newUniqueNum = {num += 1; num}
}
注:类和它的伴生对象可以相互访问私有特性;它们必须存在于同一个源文件中;类的伴生对象可以被访问,但并不在作用域中,Num类必须通过Num.newUniqueNum而不是直接用newUniqueNum来调用伴生对象的方法。
扩展类或特质的对象
一个对象可以扩展类以及一个或多个特质,其结果是一个扩展了指定类以及特质的类的对象,同时拥有在对象中给出的所有特性。一个有用的场景是给出可被共享的缺省对象。举例在来说,考虑在程序中引入一个可撤销动作的类,默认情况下什么都不做,对于这个行为你只需要一个实例即可,可通过对象扩展类实现。如下,
//定义供扩展的抽象类
abstract class UndoableAction(val description : String) {
def undo() : Unit
def redo() : Unit
}
//DoNthingAction扩展自UndoableAction,默认情况下什么都不做。
object DoNothingAction extends UndoableAction("Do nothing") {
override def undo() {}
override def redo() {}
}
注:DoNothingAction对象可以被所有需要这个缺省行为的地方共用。
apply方法
我们通常会定义使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用:
Object(参数1, ..., 参数N)
通常,这样一个apply方法返回的是伴生类的对象。举例来说,Array对象定义了apply方法,让我们可以用下面这样的表达式来创建数组:
Array("a","b","c")
为什么我们不使用构造器呢?对于嵌套表达式而言,省去new关键字会方便很多。如,Array(Array(1,7), Array(2,6))。
我们来看一个示例:
class Account(val id: Int, initBalance: Double) {
private var balance = initBalance
def printTe() = println(balance)
}
object Account {
def apply(initBalance: Double) = {
new Account(1, initBalance)
}
}
object Test {
def main(args: Array[String]) {
val account = Account(2.0)
account.printTe
}
}
注:输出结果为2.0。
应用程序对象
每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String] => Unit。如,object Test {
def main(args: Array[String]) {
println("Hello, World!")
}
}
注:输出结果为Hello, World!。
除了每次都提供自己的main方法外,你也可以扩展App特质,然后将程序代码翻入构造器方法体内。如,
object Hello extends App {
println("Hello, World!")
}
注:输出结果为Hello, World!。
如果你需要命令行参数,则可以通过args属性得到,如
object Hello extends App {
if(args.length > 0){
println("Hello, " + args(0))
}
else{
println("Hello, World!")
}
}
在类文件目录下执行如下命令
scalac Hello.scala
scala -Dscala.time Hello Ming
注:输出结果为Hello, Ming。如果在你在调用该程序时设置了scala.time选项的话,程序退出时会输出消耗的时间。App特质扩展自另一个特质DelayedInit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedInit方法中。App特质的main方法捕获到命令行参数,调用delayedInit方法,并且还可以根据要求打印消耗的时间。
枚举
和Java或C++不同,Scala并没有枚举类型。但是,标准库提供了一个Enumeration助手类,可以用于产出枚举类。例如,定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。示例如下,
object Color extends Enumeration {
val Red, Yellow, Green = Value //等同于:val Red = Value; val Yellow = Value; val Green = Value;
}
注:上述示例定义了三个字段:Red、Yellow、Green,然后用Value调用将它们初始化。每次调用Value方法都返回内部类的新实例,该内部类也叫做Value。或者你也可以向Value方法传入ID、名称或者两个参数都传。如果不指定,则ID在将前一个枚举值的基础上加一,从零开始。缺省字段名称为字段的名。定义完成后,你就可以用Color.Red、Color.Yellow、Color.Green来引用枚举值了。如果这些变得冗长繁琐,则可以通过import Color._语句直接引入枚举类,然后直接调用Red等字段即可。记住枚举的类型是Color.Value而不是Color,后者是握有这些值的对象。
完整示例1:
object Color extends Enumeration {
val Red = Value(0, "Red")
val Yellow = Value("Yellow")
val Green = Value("Green")
}
object TestEnum {
def main(args: Array[String]): Unit = {
for(color <- Color.values) {
if(color.id == 2){
print(color.id + ": " + color)
}
else{
print(color.id + ": " + color + ",")
}
}
}
}
注:输出结果为0: Red,1: Yellow,2: Green。
完整示例2:
object Color extends Enumeration {
val Red = Value(0, "Red")
val Yellow = Value("Yellow")
val Green = Value("Green")
}
import Color._
object TestEnum {
def main(args: Array[String]): Unit = {
for(color <- values) {
if(color.id == 2){
print(color.id + ": " + color + ".")
}
else{
print(color.id + ": " + color + ",")
}
}
}
}
注:输出结果为0: Red,1: Yellow,2: Green。上述示例中,你可以直接使用Red、Yellow、Green。
完整示例3:
object Color extends Enumeration {
type Color = Value //定义类型别名
val Red = Value(0, "Red")
val Yellow = Value("Yellow")
val Green = Value("Green")
}
import Color._
object action {
def doWhat(color: Color) = {
if(color == Red) "stop"
else if(color == Yellow) "hurry up"
else "go"
}
}
object TestEnum {
def main(args: Array[String]) {
println(action.doWhat(Color.Green))
}
}
注:输出结果为go。上述示例中使用type关键字定义类型别名,则该枚举的类型变成了Color.Color。
完整示例4:
object Color extends Enumeration {
type Color = Value
val Red = Value(0, "Red")
val Yellow = Value("Yellow")
val Green = Value("Green")
}
object TestEnum {
def main(args: Array[String]) {
print(Color(0) + ":" + Color.withName("Yellow"))
}
}
注:输出结果为Red:Yellow。其中,Color(0)将调用Enumeration.apply。