9.2 独立对象和伴生对象

前面提到的 MarkerFactory 是一个独立对象(stand-alone object)。它和任何类都没有自动的联系,尽管我们用它来管理 Marker 的实例。

可以选择将一个单例关联到一个类。

这样的单例,其名字和对应类的名字一致,因此被 称为伴生对象(companion object)。相应的类被称为伴生类。我们在后面可以看到这种方式非常强大。

在前面的例子中,我们可以通过伴生对象来规范 Marker 实例的创建。

类与其伴生对象间没有边界---它们可以相互访问对方私有字段和方法。

一个类的构造器,包括主构造器,也可以标记为 private。 我们可以结合这两个特性来解决前一节末尾特别提出的问题。

下面是使用一个伴生对象对 Marker 这个例子进行的重写。

import scala.collection._

// 主构造函数私有. 将来只能在伴生对象中访问
class Marker private(val color: String) {
    println(s"Creating ${this}")

    override def toString = s"marker color $color"

}

object Marker {

    private val markers = mutable.Map(
        "red" -> new Marker("red"),
        "blue" -> new Marker("blue"),
        "yellow" -> new Marker("yellow"))

    def getMarker(color: String): Marker = markers.getOrElseUpdate(color, new Marker(color))

    def main(args: Array[String]): Unit = {
        println(Marker getMarker "blue")
        println(Marker getMarker "blue")
        println(Marker getMarker "red")
        println(Marker getMarker "red")
        println(Marker getMarker "green")
    }
}

说明:

  • Marker 的构造器被声明为 private;然而,它的伴生对象可以访问它。

  • 因此,我们可 以在伴生对象中创建 Marker 的实例。

  • 如果试着在类或者伴生对象之外创建 Marker 的实例, 就会收到错误提示。

  • 每一个类都可以拥有伴生对象,伴生对象和相应的伴生类可以放在同一个文件中。在 Scala 中,伴生对象非常常见,并且通常提供一些类层面的便利方法。

  • 伴生对象还能作为一非常好的变通方案,弥补 Scala 中缺少 static 成员的事实


Scala 中的 static

Scala 没有 static 关键字,直接在一个类中允许 static 字段和 static 方法会破坏 Scala 提供的纯面向对象模型。

与此同时,Scala 通过单例对象和伴生对象完整支持类级别的操作和属性。

如果能够从 Marker 中获得所支持的颜色,就非常棒。

但是,直接在这个类的任何实例中查询并没有意义,因为这是一个类级别的操作。

换句话说,如果我们是在用 Java 写代码,就会在类 Marker 中把这个查询方法写成一个 static 方法。

但是,Scala 并不提供 static。一开始就是这样设计的,以至于这些方法在单例对象和伴生对象中作为常规方法存在。

我们来改一下 Marker 这个例子,并在伴生对象 中创建一些方法。

package scala.bj2

import scala.collection._

// 主构造函数私有. 将来只能在伴生对象中访问
class Marker private(val color: String) {
    println(s"Creating ${this}")

    override def toString = s"marker color $color"

}

object Marker {

    private val markers = mutable.Map(
        "red" -> new Marker("red"),
        "blue" -> new Marker("blue"),
        "yellow" -> new Marker("yellow"))

    def apply(color: String): Marker = markers.getOrElseUpdate(color, new Marker(color))

    def supportedColors = markers.keys

    def main(args: Array[String]): Unit = {
        println(Marker.supportedColors)
        println(Marker("blue"))
        println(Marker("yellow"))
    }
}

说明:

  • println(s"Supported colors are : ${Marker.supportedColors}") 可以直接在外面这样调用刚刚创建的方法, 就像在调用 Java 的静态方法.

apply方法

如果不用 new 关键字就可以创建伴生类的实例, 是比较舒服的操作。 特殊的 apply()方法就是达到这种效果的关键。

在前面的例子中, 当我们调用 Marker ("blue")时,实际上在调用 Marker.apply("blue")。这是一种创建或者获得实例的轻量级语法。

在字节码层面上,单例中方法会被创建为 static 方法。这从与 Java 的互操作性上讲,是一个好消息。



小结:

  1. Scala 中伴生对象采用object关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生对象名称直接调用

  2. 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致

  3. 伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问

  4. 从语法角度来讲,所谓的伴生对象其实就是类的静态方法和静态属性的集合

  5. 伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。

  6. 类和伴生对象之间可以互相访问对方的私有成员.

Copyright © 尚硅谷大数据 2019 all right reserved,powered by Gitbook
该文件最后修订时间: 2019-04-12 14:22:19

results matching ""

    No results matching ""