1. 概要

このチュートリアルでは、Scalaのケースオブジェクトオブジェクトの概念を探ります。

それらの主な違いを確認し、ケースオブジェクトの追加機能について説明します。

2. Scalaオブジェクト

Scalaオブジェクトは、単一のインスタンスを1つだけ持つクラスを表します。 オブジェクトは、最上位の値としてのシングルトンです。

オブジェクトの例を次に示します。

object Car {
  val numberOfWheels = 4

  def run(): Unit = {
    val currentDateAndTime: Date = new Date(System.currentTimeMillis())
    println(s"I am a new car running on $currentDateAndTime!")
  }
}

大きな違いオブジェクト間とクラスそれはオブジェクトでは、コンストラクターにパラメーターを割り当てることはできません。 シングルトンとしてScalaオブジェクトを作成することは、不変のインスタンスに最も役立ち、1回だけ存在します。

3. ケースオブジェクトとオブジェクト

この章では、最初に簡単なケースオブジェクトの例を示します。 次に、ケースオブジェクトオブジェクトの主な違いを比較します。

単純なケースオブジェクトを見てみましょう。

case object Bicycle {
  val numberOfWheels = 2

  def run(): Unit = {
    val currentDateAndTime: Date = new Date(System.currentTimeMillis())
  }
}

観察できるように、 前の例との唯一の構文の違いは、オブジェクトの前の単語の大文字小文字の区別です。 ケースオブジェクトは、オブジェクトのすべての機能を継承し、それらをさらに拡張します。

  • シリアル化のデフォルトの実装
  • パターンマッチング
  • 列挙
  • toStringのデフォルトの実装

これらのそれぞれを詳しく見てみましょう。

3.1. シリアル化

ケースオブジェクトはデフォルトでシリアル化可能ですが、objectはそうではありません。

class ObjectExampleUnitTest extends FlatSpec {

  "Bicyle" should "be an instance of Serializable" in {
    assert(Bicycle.isInstanceOf[Serializable])
  }

  "Car" should "not be an instance of Serializable" in {
    assert(!Car.isInstanceOf[Serializable])
  }
}

オブジェクトをシリアライズ可能にするには、シリアライズ可能特性を拡張する必要があります。

object Car extends Serializable {
  val numberOfWheels = 4

  def run(): Unit = {
    val currentDateAndTime: Date = new Date(System.currentTimeMillis())
  }
}

3.2. パターンマッチング

次に、ケースオブジェクトのもう1つの強力な機能であるパターンマッチングについて見ていきます。

まず、新しい抽象クラス Vehicle を作成し、BicycleCarを拡張します。

abstract class Vehicle
object Car extends Vehicle
case object Bicycle extends Vehicle

次に、messageVehicleという新しいメソッドを作成します。

def messageVehicle(vehicle: Vehicle): Unit = {
  vehicle match {
    case Car => println("send message to Car")
    case Bicycle => println("send message to Bicycle")
  }
}

このメソッドは、CarまたはBicycleの単一のパラメーターを取得します。 パターンマッチングは、Vehicle。の具象インスタンスからのタイプに基づいてprintln出力を決定します。

たとえば、次のように実行します。

messageVehicle(Car)

出力を取得します。

send message to Car

3.3. 列挙

オブジェクトとケースオブジェクトの両方を使用して列挙型を作成できます。ただし、それらの実装方法には大きな違いがあります。

まず、オブジェクトを使用して列挙を実装する方法を見てみましょう。

object FlyingObject extends Enumeration {
  val airplane = Value("AP")
  val bird = Value("BD")
  val drone = Value("DE")
}

前の例からわかるように、 Scalaでは、Javaとは異なり、enumキーワードはありません。 単に列挙型クラスを提供します。 それを拡張することで、クラスのメンバーを繰り返すことができます。 さらに、その値を呼び出すことで各要素にアクセスできます。 または、IDを呼び出すことでアクセスすることもできます。

${FlyingObject.bird.id}

デフォルトでは、IDは列挙用に段階的に作成されます。 IDを変更できます。

object FlyingObjectChangingID extends Enumeration {
  val airplane = Value(2, "AP")
  val bird = Value(3, "BD")
  val drone = Value(1, "DE")
}

さて、列挙の順序は次のとおりです。 「DE、AP、BD」。 ご覧のとおり、値の順序は最初の印刷の反復とは異なります。

パターンマッチングを行っているときの列挙の問題を見てみましょう。

コンパイル時の完全な一致チェックはありません。 したがって、次のように記述します。

def nonExhaustive(objects: FlyingObject.Value) {
  objects match {
    case FlyingObject.airplane => println("I am an airplane")
    case FlyingObject.bird => println("I am a bird")
  }
}

nonExhaustive(FlyingObject.drone)

コードはエラーなしでコンパイルされますが、実行時にscala.MatchErrorがスローされます

この問題を回避するために、密閉されたケースオブジェクトを使用できます。例を見てみましょう。

sealed trait FlyingCaseObjects
case object AirplaneCaseObject extends FlyingCaseObjects
case object BirdCaseObject extends FlyingCaseObjects
case object DroneCaseObject extends FlyingCaseObjects

同じファイル内の封印された特性のみを拡張できます。 コンパイラは、考えられるすべてのサブタイプを認識し、パターンマッチングが完全でない場合に警告を提供します。 次の方法では、徹底的な警告が表示されます。

def sealedTraitMatch(flyingObject: FlyingCaseObjects): Unit = {
  flyingObject match {
    case AirplaneCaseObject => println("I am an airplane")
    case BirdCaseObject => println("I am a bird")
  }
}

3.4. toStringメソッドの実装

最後に、ケースオブジェクトオブジェクトに対する最後の追加機能( toString メソッドのデフォルト実装)を見てみましょう。

println(Car)
println(Bicycle)

これにより、 toStringメソッドがトリガーされ、はデフォルトでオブジェクトの名前を提供します。 これで、次の出力が表示されます。

com.baeldung.scala.caseobject.Car$@2f7c7260
Bicycle

4. 結論

このチュートリアルでは、ケースオブジェクトオブジェクトをさまざまな目的で使用する方法を説明しました。

最初に、オブジェクトケースオブジェクトの構文の違いを確認しました。次に、パターンマッチングと列挙を作成するさまざまな方法を検討しました。

最後に、caseオブジェクトtoStringメソッドのデフォルトの実装について説明しました。

いつものように、コードはGitHubで見つけることができます