天天看點

Scala基礎教程 - 單例對象、伴生類是什麼?定義一個單例對象伴生對象Java 程式員的注意事項

單例對象是一種特殊的類,有且隻有一個執行個體。和惰性變量一樣,單例對象是延遲建立的,當它第一次被使用時才建立。

當對象定義于頂層時(即未包含在其他類中),單例對象隻有一個執行個體。

當對象定義在一個類或方法中時,單例對象表現得和惰性變量一樣。

定義一個單例對象

一個單例對象是就是一個值。單例對象的定義方式很像類,但是使用關鍵字 object:

object Box      

下面例子中的單例對象包含一個方法:

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

方法 info 可在程式中的任何地方被引用。像這樣建立功能性方法是單例對象的一種常見用法。

下面讓我們來看看如何在另外一個包中使用 info 方法:

// 因為 import 語句,方法 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語句要求被導入的辨別具有一個“穩定路徑”,一個單例對象由于全局唯一,是以具有穩定路徑。

注意:如果一個 object 沒定義在頂層而是定義在另一個類或者單例對象中,那麼這個單例對象和其他類普通成員一樣是“路徑相關的”。這意味着有兩種行為,class Milk 和 class OrangeJuice,一個類成員 object NutritionInfo “依賴”于包裝它的執行個體,要麼是牛奶要麼是橙汁。 milk.NutritionInfo 則完全不同于oj.NutritionInfo。

伴生對象

Scala 裡,在一個源代碼檔案中同時定義相同名字的 class 和 object 的用法被稱為伴生(Companion)。

Class 對象被稱為伴生類,它和 Java 中的類是一樣的。

Object 對象是一個單例對象,用于儲存一些靜态變量或靜态方法。如果用 Java ,必須要編寫兩個類才能實作,LogSegment 和 LogSegmentUtils。而在 Scala 中,使用伴生即可。

當一個單例對象和某個類共享一個名稱時,這個單例對象稱為伴生對象。 同理,這個類被稱為是這個單例對象的伴生類。類和它的伴生對象可以互相通路其私有成員。使用伴生對象來定義那些在伴生類中不依賴于執行個體化對象而存在的成員變量或者方法。

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 {
  // 伴生對象 object Email 包含一個工廠方法 fromString 
  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")
}      

類和它的伴生對象必須定義在同一個源檔案。如果需要在 REPL 裡定義類和其伴生對象,需要将它們定義在同一行或者進入 :paste 模式。

Java 程式員的注意事項

在 Java 中 static 成員對應于 Scala 中的伴生對象的普通成員。

在 Java 代碼中調用伴生對象時,伴生對象的成員會被定義成伴生類中的 static 成員。這稱為靜态轉發。這種行為發生在當你自己沒有定義一個伴生類時。

參考

https://docs.scala-lang.org/zh-cn/tour/singleton-objects.html