问题导读:
1、apply方法的含义是什么?
2、Scala如何产出枚举类型?
3、Scala如何输出所有枚举值的集?
资料来源:Scala学习(六)---Scala对象
Scala中的对象
摘要: 在本篇中,你将会学到何时使用Scala的object语法结构。在你需要某个类的单个实例时,或者想为其他值或函数找一个可以挂靠的地方时,你就会用到它。本篇的要点包括: 1). 用对象作为单例或存放工具方法 2). 类可以拥有—个同名的伴生对象 3). 对象可以扩展类或特质 4). 对象的apply方法通常用来构造伴生类的新实例 5). 如果不想显式定义main方法,可以用扩展App特质的对象 6). 你可以通过扩展Enumeration对象来实现枚举
1、单例对象
Scala没有静态方法或静态字段,你可以用object这个语法结构来达到同样目的。对象定义了某个类的单个实例,包含了你想要的特性。例如: [mw_shl_code=java,true]object Accounts {
private var lastNumber=0
def newUniqueNumber() = { lastNumber+=1, lastNumber}
}[/mw_shl_code] 当你在应用程序中需要一个新的唯一账号时,调用Accounts.newUniqueNumber()即可。对象的构造器在该对象第一次被使用时调用。在本例中,Accounts的构造器在Accounts.newUniqueNumber()的首次调用时执行。如果一个对象从未被使用,那么其构造器也不会被执行。 对象本质上可以拥有类的所有特性,它甚至可以扩展其他类或特质。只有一个例外:你不能提供构造器参数。对于任何你在Java或C++中会使用单例对象的地方,在Scala中都可以用对象来实现: ■ 作为存放工具函数或常量的地方 ■ 高效地共享单个不可变实例 ■ 需要用单个实例来协调某个服务时,可参考单例模式 注意:很多人都看低单例模式。Scala提供的是工具,可以做出好的设计,也可以做出糟糕的设计,你需要做出自己的判断。
2、伴生对象
在Java或C++中,你通常会用到既有实例方法又有静态方法的类。在Scala中,你可以通过类和与类同名的"伴生"对象来达到同样的目的。例如: [mw_shl_code=java,true]class Account {
val id=Account.newUniqueNumber()
private var balance =0
def deposit (amount: Double) { balance+=amount }
}
object Account{ // 伴生对象
private var lastNumber=0
private def newUniqueNumber() = { lastNumber+=1;lstNumber }
}[/mw_shl_code] 类和它的伴生对象可以相互访问私有特性。它们必须存在于同一个源文件中。这说明了类的伴生对象可以被访问,但并不在作用域当中。举例来说,Account类必须通过Account.newUniqueNumber()而不是直接用newUniqueNumber()来调用伴生对象的方法。
3、扩展类或特质的对象
一个object可以扩展类以及一个或多个特质,其结果是一个扩展了指定类以及特质的类的对象,同时拥有在对象定义中给出的所有特性。一个有用的使用场景是给出可被共享的缺省对象。举例来说,考虑在程序中引入一个可撤销动作的类: [mw_shl_code=java,true]abstract class UndoableAction (val description: String) {
def undo() : Unit
def redo() : Unit
}[/mw_shl_code] 默认情况下可以是"什么都不做"。当然了,对于这个行为我们只需要一个实例即可: [mw_shl_code=java,true]object DoNothingAction extends UndoableAction("Do nothing") {
override def undo () {}
override def redo () {}
}[/mw_shl_code] DoNothingAction对象可以被所有需要这个缺省行为的地方共用。 [mw_shl_code=java,true]val actions=Map( "open" -> DoNothingAction,"save" -> DoNothingAction,…) // 打开和保存功能尚未实[/mw_shl_code]
4、apply方法
1)、apply含义 我们通常会定义和使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用: Object(参数1,…,参数N)
通常,这样—个apply方法返回的是伴生类的对象。举例来说,Array对象定义了apply方法,让我们可以用下面这样的表达式来创建数组: [mw_shl_code=java,true]Array("Mary", "had", "a", "little", "lamb")[/mw_shl_code] 为什么不用构造器呢?对于嵌套表达式而言,省去new关键字会方便很多,例如: [mw_shl_code=java,true]Array (Array (1, 7),Array (2, 9))[/mw_shl_code] 注意:Array(100)和new Array(100)很容易搞混。前一个表达式调用的是apply(100),输出一个单元素整数100的Array[Int]。而第二个表达式调用的是构造器this(100),结果是Array[Nothing],包含了100个null元素。 5、apply示例 这里有一个定义apply方法的示例: [mw_shl_code=java,true]class Account private (val id: Int, initialBalance: Double) {
private var balance=initiaIBalance
………
}
object Account { //伴生对象
def apply (initialBalance: Double) =
new Account (newUniqueNumber(), initialBalance)
}[/mw_shl_code] 这样一来你就可以用如下代码来构造账号了: [mw_shl_code=java,true]val acct = Account (1000.0)[/mw_shl_code]
6、应用程序对象
1)、main方法 每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String]=> Unit: [mw_shl_code=java,true]object Hello{
def main (args: Array[String]) {
println("Hello, World! ")
}
}[/mw_shl_code] 2)、扩展App特质 除了每次都提供自己的main方法外,你也可以扩展App特质,然后将程序代码放人构造器方法体内: [mw_shl_code=java,true]object Hello extends App{
println("Hello, World! ")
}[/mw_shl_code] 3)、命令行参数 如果你需要命令行参数,则可以通过args属性得到: [mw_shl_code=java,true]object Hello extends App{
if (args.length > 0)
println("Hello, "+args (0))
else
println("Hello, World! ")
}[/mw_shl_code] 4)、时间特质 如果你在调用该应用程序时设置了scala.time选项的话,程序退出时会显示逝去的时间 [mw_shl_code=java,true]scalac Hello.scala
scala -Dscala.time Hello Fred
Hello, Fred
[total 4ms][/mw_shl_code] 所有这些涉及一些小小的魔法。App特质扩展自另一个特质Delayedlnit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedlnit方法中。App特质的main方法捕获到命令行参数,调用delayedlnit方法,并且还可以根据要求打印出逝去的时间。在较早版本的Scala有一个Application特质来达到同样的目的。那个特质是在静态初始化方法中执行程序动作,并不被即时编译器优化,因此应尽量使用新的App特质。
7、枚举
1)、枚举定义初始化 和Java或C++不同,Scala并没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举。定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。例如: [mw_shl_code=java,true]object TrafficLightColor extends Enumeration (
val Red, Yellow,Green=Value
}[/mw_shl_code] 在这里我们定义了三个字段:Red、Yellow和Green,然后用Value调用将它们初始化。这是如下代码的简写: [mw_shl_code=java,true]val Red = Value
val Yellow = Value
val Green = Value[/mw_shl_code] 每次调用Value方法都返回内部类的新实例,该内部类也叫做Value。或者,你也可以向Value方法传人ID、名称,或两个参数都传: [mw_shl_code=java,true]val Red = Value (0, "Stop")
val Yellow = Value(10) // 名称为"Yellow"
val Green = Value("Go") // ID为11[/mw_shl_code] 如果不指定,则ID在将前一个枚举值基础上加一,从零开始,缺省名称为字段名。 2)、枚举引用 定义完成后,你就可以用TrafficLightColor.Red、TrafficLightColor.Yellow等来引用枚举值了。如果这些变得冗长烦琐,则可以用如下语句直接引入枚举值: [mw_shl_code=java,true]import TrafficLightColor._[/mw_shl_code] 需要注意的是:枚举的类型是TrafficLightColor.Value而不是TrafficLightColor,后者是握有这些值的对象。有人推荐增加一个类型别名: [mw_shl_code=java,true]object TrafficLightColor extends Enumeration {
TrafficLightColor = Value
val Red, Yellow, Green=Value
}[/mw_shl_code] 现在枚举的类型变成了TraffcLightColor.TrafficLightColor,但仅当你使用import语句时这样做才显得有意义。例如: [mw_shl_code=java,true]import TrafficLightColor._
def doWhat (color : TrafficLightColor) = {
if (color==Red) "stop"
else if (color==Yellow) "hurry up"
else "go"
}[/mw_shl_code] 3)、访问枚举 枚举值的ID可通过id方法返回,名称通过toString方法返回。对TrafficLightColor.values的调用输出所有枚举值的集: [mw_shl_code=java,true]for(c <- TrafficLightColor.values)
println (c.id+":"+c)[/mw_shl_code] 最后,你可以通过枚举的ID或名称来进行查找定位,以下两段代码都输出TrafficLightColor.Red对象: [mw_shl_code=java,true]TrafficLightColor (0) // 将调用Enumeration.apply
TrafficLightColor.withName( "Red")[/mw_shl_code]
|