Tour of Scala

シングルトンオブジェクト

Language

オブジェクトは丁度1つのインスタンスを持つクラスです。 それはlazy valのように参照された際に遅れて作られます。

トップレベルにあるオブジェクトは、シングルトンです。 クラスのメンバーやローカル変数としてのオブジェクトは、lazy valと全く同じように振る舞います。

シングルトンオブジェクトの定義

オブジェクトは値です。オブジェクトの定義はクラスのように見えますが、キーワードobjectを使います。

object Box

これはメソッドを持つオブジェクトの例です。

package logging

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

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文import logging.Logger.infoにより、infoメソッドが見えるようになります。

import文には取り込むシンボルへの”変動しないパス”が必要であり、オブジェクトは変動しないパスとなります。

注意:objectがトップレベルではなく他のクラスやオブジェクトにネストされている時、そのオブジェクトは他のメンバーのように”経路依存性”があります。 これは2種類の飲み物class 牛乳class オレンジジュースが与えられた場合、クラスメンバーのobject 栄養素はそれが属するインスタンス、すなわち牛乳またはオレンジジュースのいずれかに依存することを意味します。 milk.NutritionInfooj.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("scala.center@epfl.ch")
scalaCenterEmail match {
  case Some(email) => println(
    s"""Registered an email
       |Username: ${email.username}
       |Domain name: ${email.domainName}
     """.stripMargin)
  case None => println("Error: could not parse email")
}

object EmailはファクトリーfromStringを持ち、StringからEmailインスタンスを作ります。 パースエラーの場合も考えて、返り値の型をOption[Email]とします。

注意:クラスまたはオブジェクトがコンパニオンを持つ場合、クラス、オブジェクトの両方は同じファイルの中に定義されていなければなりません。 REPL内でコンパニオンを定義する場合は、それらを同じ行で定義するか、:pasteモードに入ります。

Javaプログラマのための注意事項

JavaにおけるstaticメンバーはScalaではコンパニオンオブジェクトの一般メンバーとして作られています。

Javaのコードからコンパニオンオブジェクトを使う場合、メンバーはコンパニオンクラス内でstatic識別子を用いて定義されます。 これはstatic forwardingと呼ばれます。 これはコンパニオンクラスを自分で定義していなかったとしても起きます。

Contributors to this page: