case classについて

Scala 3 (Dotty 0.26.0-RC1) 2.13.3 2.12.12
最終更新:2020年5月19日

[AD] scalapediaでは記事作成ボランティアを募集しています

この記事ではcase classについて解説します。

case classは不変なデータを表現する

case classは不変なデータを表現するための便利な機能です。
少量のコードで簡単に不変なデータを扱うことができます。

case classを定義する

case classは、以下のようにcaseキーワードを先頭に付けてクラスを宣言することで定義できます。

case class Owner(name: String) case class Book(title: String, owner: Owner)

case classは通常のクラスと比較して簡単にインスタンスを作成できます。
通常のクラスはnewキーワードをつけてインスタンスを生成しますが、case classはインスタンスを作成する際にnewキーワードをつける必要がありません。
また、インスタンスを作成する際に指定したパラメーターは、「不変なpublicメンバー」になります。
以下のようにインスタンスを作成する際に指定したパラメーターへクラスの外からアクセスすることができます。

val owner = Owner("Taro") println(owner.name)

実行結果は、以下のようになります。

Taro

case classを比較する

次にcase classのインスタンスを比較する方法を説明します。

case classは通常のクラスと比較して簡単にインスタンスの比較ができます。
case classのインスタンスは、==を使用して比較します。

では、「case classを定義する」で定義したBookOwnerを比較してみます。

以下のように2つのBookを比較する関数を定義します。

def compare(book1: Book, book2: Book): Unit = { if(book1 == book2) { println("same") } else { println("not same") } }

定義した関数を実行してみます。

compare(Book("Hello World!", Owner("Taro")), Book("Hello World!", Owner("Taro")))

インスタンスの参照先が違っていても、構造が同じであれば同じものと判定されます。

same

次にtitleは同じでOwnerが違うインスタンスを指定して関数を呼び出してみます。

compare(Book("Hello World!", Owner("Taro")), Book("Hello World!", Owner("Hanako")))

結果は以下の通り、違うものと判断されます。

not same

case classのインスタンスを文字列に変換する

Scalaのすべてのクラスは、Anyクラスの子クラスです。
そのため、case classでもAnyクラスのtoStringメソッドを使用してインスタンスを文字列で表現することができます。

通常のクラスのtoStringメソッドは、以下のようなハッシュ値がついた文字列を返しますが、case classtoStringメソッドは、インスタンスの内容が理解できる文字列を返します。

Book@20ed0575

先ほどのBookクラスのインスタンスに対してtoStringを呼び出してみます。

val book = Book("Hello World!", Owner("Taro")) println(book.toString())

case classtoStringは、人がインスタンスの内容を理解できる形式の文字列に変換してくれます。

Book(Hello World!,Owner(Taro))

case classをコピーする

case classのインスタンスをコピーする方法を説明します。

case classを使用すると元のインスタンスをコピーして、簡単に別のインスタンスを作成することができます。

以下のBookクラスのインスタンスをコピーしてみます。

val book1 = Book("Hello World!", Owner("Taro"))

case classのインスタンスは、copyメソッドを使用してコピーすることができます。
以下の例では、「book2」は「book1」と同じ内容になります。

val book2 = book1.copy() println(book1.toString()) println(book2.toString())

結果は、以下のとおり「book1」と「book2」は同じことがわかります。

Book(Hello World!,Owner(Taro)) Book(Hello World!,Owner(Taro))

また、コピーする際にインスタンスの一部のみを変更することができます。
以下の例では、先ほどの「book1」の「owner」を変更しています。

val book3 = book1.copy(owner = Owner("Hanako")) println(book3.toString())

結果は以下の通り、「book3」の「title」は「book1」と同じですが「owner」が違うことがわかります。

Book(Hello World!,Owner(Hanako))

copyメソッドはcase classに定義したメソッドの戻り値を作成するためによく利用されます。

以下の例では、changeOwnerメソッドでownerを変更したインスタンスを返しています。
インスタンスのメンバーの値をメソッド内で変更するのではなく、インスタンスのコピーを返すことでメソッドの復帰値のインスタンスは不変であることが保証されます。

case class Book(title: String, owner: Owner) { def changeOwner(newOwner: Owner): Book = copy(owner = newOwner) }

「book1」に対してchangeOwnerを呼び出してみます。

val book4 = book1.changeOwner(Owner("Hanako")) println(book4.toString())

結果は以下の通り、「book4」の「title」は「book1」と同じですが「owner」が違うことがわかります。

Book(Hello World!,Owner(Hanako))

case classをパターンマッチで利用する

最後にcase classでパターンマッチをする方法を説明します。

以下で定義したBookクラスのインスタンスから「title」と「owner」の「name」を取り出してみます。

val book = Book("Hello World!", Owner("Taro"))

以下のようにパターンマッチを利用して「title」と「owner」の「name」を取り出すことができます。

val Book(title, Owner(name)) = book println(s"titile=${title} owner=${name}")

実行結果は、以下のとおり変数「title」と「name」にインスタンスの値が設定されていることがわかります。

titile=Hello World! owner=Taro

また、match式でcase classを利用することができます。
先ほどの例をmatch式で書くと以下のようになります。

book match { case Book(title, Owner(name)) => println(s"titile=${title} owner=${name}") }

実行結果は、以下のとおり変数「title」と「name」にインスタンスの値が設定されていることがわかります。

titile=Hello World! owner=Taro

match式の詳細は、以下の記事を参照してください。

サイト内検索