第十讲 对象

在本篇中,你将会学到何时使用Scala的object语法结构。在你需要某个类的单个实例时,或者想为其他值或函数找一个可以挂靠的地方时,你就会用到它。

本章要点:

  • 用对象作为单例或存放工具方法
  • 类可以拥有—个同名的伴生对象
  • 封装、继承和多态
  • 对象的apply方法通常用来构造伴生类的新实例
  • 如果不想显式定义main方法,可以用扩展App特质的对象
  • 你可以通过扩展Enumeration对象来实现枚举

10.1 scala工具类(object)

Scala没有像java一样的静态方法或静态字段,但是可以通过object这个语法结构来达到同样目的。对象定义了某个类的单个实例或工具类。例如:

object Accounts {
  private var lastNumber = 0
  def newUniqueNumber() = {
    lastNumber += 1;
    lastNumber
  }
  def main(args: Array[String]): Unit = {
    Accounts.newUniqueNumber()
  }
}

当你在应用程序中需要一个新的唯一账号时,调用Accounts.newUniqueNumber()即可。对象的构造器在该对象第一次被使用时调用。在本例中,Accounts的构造器在Accounts.newUniqueNumber()的首次调用时执行。如果一个对象从未被使用,那么其构造器也不会被执行。

对象本质上可以拥有类的所有特性,它甚至可以扩展其他类或特质。只有一个例外:你不能提供构造器参数。对于任何你在Java或C++中会使用单例对象的地方,在Scala中都可以用对象来实现:

  • 作为存放工具函数或常量的地方
  • 高效地共享单个不可变实例
  • 需要用单个实例来协调某个服务时,可参考单例模式

10.2 伴生对象

在Java或C++中,你通常会用到既有实例方法又有静态方法的类。在Scala中,你可以通过类和与类同名的"伴生"对象来达到同样的目的。例如:

class Account {
  val id = Account.newUniqueNumber()
  private var balance:Double = 0
  def deposit(amount: Double) {
    balance += amount
  }
}

object Account { // 伴生对象
  private var lastNumber = 0
  private def newUniqueNumber() = {
    lastNumber += 1;
    lastNumber
  }
  def main(args: Array[String]): Unit = {
    var account=new Account
    account.deposit(10)//调用实例方法
    Account.newUniqueNumber();//调用静态方法
  }
}

10.3 封装、继承和多态

第一讲曾说过scala具有面向对象编程的特征,那么它当然就具有和java类似的抽象和继承,多态的特点。
例子如下:

abstract class UndoableAction(val description: String) {
  def undo(): Unit
  def redo(): Unit
}
class DoNothingAction extends UndoableAction("Do nothing") {
  override def undo() {
    print("****undo******")
  }
  override def redo() {
    print("*****redo*****")
  }
}
object UndoableAction{
    def main(args: Array[String]): Unit = {
    var action:UndoableAction=new DoNothingAction
    action.redo()
  }
}

10.4 apply方法

apply含义:我们通常会定义和使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用:
Object(参数1,…,参数N)
通常,这样—个apply方法返回的是伴生类的对象。举例来说,Array对象定义了apply方法,让我们可以用下面这样的表达式来创建数组:

Array("Mary", "had", "a", "little", "lamb")

其实和构造器所起的作用是一样的,那么为什么不用构造器呢?
对于嵌套表达式而言,省去new关键字会方便很多,例如:

Array (Array (1, 7),Array (2, 9))

注意:Array(100)和new Array(100)很容易搞混。前一个表达式调用的是apply(100),输出一个单元素整数100的Array[Int]。而第二个表达式调用的是构造器this(100),结果是Array[Nothing],包含了100个null元素。

自定义apply实例,如下:

class Account private (val id: Int, val initialBalance: Double) {
  private var balance = initialBalance
}

object Account {//伴生对象
  private var lastNumber = 0
  private def newUniqueNumber() = {
    lastNumber += 1;
    lastNumber
  }
  def apply(initialBalance: Double) =

    new Account(newUniqueNumber(), initialBalance)

  def main(args: Array[String]): Unit = {
    var account = Account.apply(2222);
    println("balance:" + account.balance + "    id:" + account.id)
    var account1 = Account(3333)//此时,不用new命令了,效果一样,默认调用的apply方法
    println("balance:" + account1.balance + "    id:" + account1.id)
  }
}

10.5 应用对象

每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为 Array[String]=> Unit:

object Hello {
  def main(args: Array[String]) {
    println("Hello, World! ")
  }
}

扩展App特质

除了每次都提供自己的main方法外,你也可以扩展App特质,然后将程序代码放人构造器方法体内:

object Hello extends App {
  println("Hello, World! ")//也可作为入口,直接执行
}

和java一样,如果你需要命令行参数,则可以通过args属性得到:

object Hello extends App {
  if (args.length > 0)
    println("Hello, " + args(0))
  else
    println("Hello, World! ")
}

如果你在调用该应用程序时设置了scala.time选项的话,程序退出时会显示逝去的时间

scalac Hello.scala

scala -Dscala.time Hello Fred

Hello, Fred

[total 4ms]

所有这些涉及一些小小的魔法。App特质扩展自另一个特质Delayedlnit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedlnit方法中。App特质的main方法捕获到命令行参数,调用delayedlnit方法,并且还可以根据要求打印出逝去的时间。

10.5 scala枚举

Scala没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举。
定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。例如:

object TrafficLightColor extends Enumeration {
  val Red, Yellow, Green = Value
}

也可以写成这样:

object TrafficLightColor extends Enumeration {
  val Red = Value
  val Yellow = Value
  val Green = Value
}

每次调用Value方法都返回内部类的新实例,该内部类也叫做Value。或者,你也可以向Value方法传人ID、名称,或两个参数都传:

    val Red = Value(0, "Stop")
    val Yellow = Value(10) // 名称为"Yellow"
    val Green = Value("Go") // ID为11

如果不指定,则ID在将前一个枚举值基础上+1,从零开始。缺省名称为字段名。
定义完成后,你就可以用TrafficLightColor.Red、TrafficLightColor.Yellow等来引用枚举值了。如果这些变得冗长烦琐,·则可以用如下语句直接引入枚举值:

object TrafficLightColor extends Enumeration {
    val Red, Yellow, Green=Value;
}
object Test {
  def doWhat(color: TrafficLightColor.Value) = {
    if (color == TrafficLightColor.Red) "stop"
    else if (color == TrafficLightColor.Yellow) "hurry up"
    else "go"
  }
  
  def main(args: Array[String]): Unit = {
    print(Test.doWhat(TrafficLightColor.Red));
  }
}

枚举值的ID可通过id方法返回,名称通过toString方法返回。对TrafficLightColor.values的调用输出所有枚举值的集:

    for (c <- TrafficLightColor.values) {
      println(c.id + ":" + c)
    }

最后,你可以通过枚举的ID或名称来进行查找定位,以下两段代码都输出TrafficLightColor.Red对象:

    print(TrafficLightColor(0))// 将调用Enumeration.apply
    print(TrafficLightColor.withName("Red"));
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容