Tour of Scala

单例对象

Language

单例对象是一种特殊的类,有且只有一个实例。和惰性变量一样,单例对象是延迟创建的,当它第一次被使用时创建。

当对象定义于顶层时(即没有包含在其他类中),单例对象只有一个实例。

当对象定义在一个类或方法中时,单例对象表现得和惰性变量一样。

定义一个单例对象

一个单例对象是就是一个值。单例对象的定义方式很像类,但是使用关键字 object

object Box

下面例子中的单例对象包含一个方法:

package logging

object Logger {
  def info(message: String): Unit = println(s"INFO: $message")
}

方法 info 可以在程序中的任何地方被引用。像这样创建功能性方法是单例对象的一种常见用法。

下面让我们来看看如何在另外一个包中使用 info 方法:

import logging.Logger.info

class Project(name: String, daysToComplete: Int)

class Test {
  val project1 = new Project("TPS Reports", 1)
  val project2 = new Project("Website redesign", 5)
  info("Created projects")  // Prints "INFO: Created projects"
}

因为 import 语句 import logging.Logger.info,方法 info 在此处是可见的。

import语句要求被导入的标识具有一个“稳定路径”,一个单例对象由于全局唯一,所以具有稳定路径。

注意:如果一个 object 没定义在顶层而是定义在另一个类或者单例对象中,那么这个单例对象和其他类普通成员一样是“路径相关的”。这意味着有两种行为,class Milkclass OrangeJuice,一个类成员 object NutritionInfo “依赖”于包装它的实例,要么是牛奶要么是橙汁。 milk.NutritionInfo 则完全不同于oj.NutritionInfo

伴生对象

当一个单例对象和某个类共享一个名称时,这个单例对象称为 伴生对象。 同理,这个类被称为是这个单例对象的伴生类。类和它的伴生对象可以互相访问其私有成员。使用伴生对象来定义那些在伴生类中不依赖于实例化对象而存在的成员变量或者方法。

import scala.math._

case class Circle(radius: Double) {
  import Circle._
  def area: Double = calculateArea(radius)
}

object Circle {
  private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
}

val circle1 = Circle(5.0)

circle1.area

这里的 class Circle 有一个成员 area 是和具体的实例化对象相关的,单例对象 object Circle 包含一个方法 calculateArea ,它在每一个实例化对象中都是可见的。

伴生对象也可以包含工厂方法:

class Email(val username: String, val domainName: String)

object Email {
  def fromString(emailString: String): Option[Email] = {
    emailString.split('@') match {
      case Array(a, b) => Some(new Email(a, b))
      case _ => None
    }
  }
}

val scalaCenterEmail = Email.fromString("[email protected]")
scalaCenterEmail match {
  case Some(email) => println(
    s"""Registered an email
       |Username: ${email.username}
       |Domain name: ${email.domainName}
     """)
  case None => println("Error: could not parse email")
}

伴生对象 object Email 包含有一个工厂方法 fromString 用来根据一个 String 创建 Email 实例。在这里我们返回的是 Option[Email] 以防有语法分析错误。

注意:类和它的伴生对象必须定义在同一个源文件里。如果需要在 REPL 里定义类和其伴生对象,需要将它们定义在同一行或者进入 :paste 模式。

Java 程序员的注意事项

在 Java 中 static 成员对应于 Scala 中的伴生对象的普通成员。

在 Java 代码中调用伴生对象时,伴生对象的成员会被定义成伴生类中的 static 成员。这称为 静态转发。这种行为发生在当你自己没有定义一个伴生类时。

Contributors to this page: