オブジェクト指向から関数型プログラミングへ『Scala 入門 (Essential Scala)』第3章 オブジェクトとクラス

[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) で公開されている [Essential Scala](https://underscore.io/books/essential-scala/) の [3 Objects and Classes](https://books.underscore.io/essential-scala/essential-scala.html#objects-and-classes) を日本語訳したものです。日本語訳においてもライセンスは同じものを継承しています。毎日少しずつ翻訳を進めており、最新の日本語訳は [Scala 入門 (Essential Scala)](http://takuya0301.github.io/essential-scala.html) にあります。

原書：[underscoreio/essential-scala](https://github.com/underscoreio/essential-scala)
訳書：[takuya0301/essential-scala-ja](https://github.com/takuya0301/essential-scala-ja)

# ライセンス

Written by [Dave Gurnell](http://twitter.com/davegurnell) and [Noel Welsh](http://twitter.com/noelwelsh). Copyright [Underscore Consulting LLP](http://underscore.io), 2015-2017.
Translated by [Takuya Tsuchida](http://twitter.com/takuya0301). Copyright Takuya Tsuchida, 2020.
![CC BY-SA 4.0 logo](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)
This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.

# 目次

[序文](https://qiita.com/takuya0301/items/ddbe5493273d317cbb06)
[第1章 はじめに](https://qiita.com/takuya0301/items/6528b130f43dc5a3cb48)
[第2章 式・型・値](https://qiita.com/takuya0301/items/08e3101f84193f9e7346)
[第3章 オブジェクトとクラス](https://qiita.com/takuya0301/items/df373bf12a2560647c9e)
第4章 トレイトによるデータモデリング
第5章 シーケンス処理
第6章 コレクション
第7章 型クラス

# オブジェクトとクラス

前章では、オブジェクトを作成し、メソッド呼び出しを通じてオブジェクトを作用させる方法を見ました。本章では、**クラス (class)** を使用してオブジェクトを抽象化する方法を見ていきます。クラスはオブジェクトを構築するためのテンプレートです。クラスが与えられれば、同じ型を持ち、共通のプロパティを持つ多くのオブジェクトをつくることができます。

## クラス

クラスは、似たようなメソッドやフィールドを持つオブジェクトを生成するためのテンプレートです。Scala では、クラスもまた型を定義し、クラスから生成されたすべてのオブジェクトは同じ型を共有します。これによって、前章の演習「やぁ、人間」が持つ問題を克服することができます。

### クラスを定義する

これは、シンプルな `Person` クラスの宣言です。

```scala
class Person {
  val firstName = "Noel"
  val lastName = "Welsh"
  def name = firstName + " " + lastName
}
```

オブジェクト宣言のように、クラス宣言は名前（この場合 `Person`）を束縛し、また式ではありません。しかしながら、オブジェクト名と違って、式の中でクラス名を使用することはできません。クラスは値ではなく、クラスは異なる**名前空間 (namespace)** に住んでいます。

```scala
Person
// <console>:13: error: not found: value Person
//        Person
//        ^
```

`new` 演算子を使用して、新しい `Person` オブジェクトを生成できます。オブジェクトは値なので、通常の方法でそれらのメソッドやフィールドにアクセスできます。

```scala
val noel = new Person
// noel: Person = Person@15bfe8fd

noel.firstName
// res1: String = Noel
```

オブジェクトの型が `Person` であることに注意してください。印字された値には、`@xxxxxxxx` という形式のコードが含まれており、そのオブジェクトを特定する一意の識別子です。`new` を呼び出すたびに、同じ型の別オブジェクトが生成されます。

```scala
noel
// res2: Person = Person@15bfe8fd

val newNoel = new Person
// newNoel: Person = Person@60cea738

val anotherNewNoel = new Person
// anotherNewNoel: Person = Person@22fe5689
```

これは、引数として任意の `Person` を受け取るメソッドを書けることを意味します。

```scala
object alien {
  def greet(p: Person) =
    "Greetings, " + p.firstName + " " + p.lastName
}
```

```scala
alien.greet(noel)
// res3: String = Greetings, Noel Welsh

alien.greet(newNoel)
// res4: String = Greetings, Noel Welsh
```

---

#### :information_source: Java ヒント

Scala クラスはすべて `java.lang.Object` の派生クラスであり、ほとんどの場合、Scala と同様に Java からも使用できます。`Person` におけるデフォルト印字の振る舞いは、`java.lang.Object` に定義されている `toString` メソッドに由来しています。

---

### コンストラクター

現状、`Person` クラスは全然役に立ちません。新しいオブジェクトを好きなだけ生成することができますが、すべて同じ `firstName` と `lastName` を持つためです。それでは、それぞれの人に異なる名前を与えるにはどうすればいいのでしょうか？

その解決策は、**コンストラクター (constructor)** を導入することで、それは新しいオブジェクトを生成するときに引数を渡すことを可能にします。

```scala
class Person(first: String, last: String) {
  val firstName = first
  val lastName = last
  def name = firstName + " " + lastName
}
```

```scala
val dave = new Person("Dave", "Gurnell")
// dave: Person = Person@258c10ba

dave.name
// res5: String = Dave Gurnell
```

コンストラクター引数 `first` と `last` はクラスの本体でのみ使用できます。オブジェクトの外側からデータにアクセスするには、`val` や `def` を使用してフィールドやメソッドを宣言しなければなりません。

コンストラクター引数とフィールドはしばしば冗長です。幸運なことに、Scala は両方を一度に宣言する便利な略記法を提供してくれています。コンストラクター引数の前に `val` キーワードをつけることで、Scala はそれらのフィールドを自動的に定義してくれるようになります。

```scala
class Person(val firstName: String, val lastName: String) {
  def name = firstName + " " + lastName
}
```

```scala
new Person("Dave", "Gurnell").firstName
// res6: String = Dave
```

`val` フィールドは**不変 (immutable)** で、一度初期化された後にそれらの値を変更することはできません。Scala は**可変 (mutable)** フィールドを定義するために `var` キーワードも提供しています。

Scala プログラマーは、不変かつ副作用のないコードを書くことを好む傾向があるので、置換モデルを使用してそれらを導出することができます。本書では、もっぱら不変な `val` フィールドに焦点を当てていきます。

---

#### :information_source: クラス宣言文法

クラスを宣言するための文法は、

```scala
class Name(parameter: type, ...) {
  declarationOrExpression ...
}
```

か

```scala
class Name(val parameter: type, ...) {
  declarationOrExpression ...
}
```

です。ここで、

- `Name` はクラスの名前
- `parameter` はコンストラクター引数に与えられた名前（オプション）
- `type` はコンストラクター引数の型
- `declarationOrExpression` は宣言や式（オプション）

とします。

---

### デフォルト引数とキーワード引数

Scala のすべてのメソッドとコンストラクターは、**キーワード引数 (keyword parameter)** と**デフォルト引数値 (default parameter value)** に対応しています。

メソッドやコンストラクターを呼び出すときに、任意の順番で引数を指定するための**キーワードとして引数名を使用する**ことができます。

```scala
new Person(lastName = "Last", firstName = "First")
// res7: Person = Person@5cce4fcc
```

これは、下記のように定義された**デフォルト引数値**と組み合わせて使用するといっそう便利です。

```scala
def greet(firstName: String = "Some", lastName: String = "Body") =
  "Greetings, " + firstName + " " + lastName + "!"
```

引数がデフォルト値を持つのであれば、メソッド呼び出しでその引数を省略できます。

```scala
greet("Busy")
// res8: String = Greetings, Busy Body!
```

デフォルト引数値とキーワードを組み合わせることで、前の引数を省略し、後の引数だけに値を渡すこともできます。

```scala
greet(lastName = "Dave")
// res9: String = Greetings, Some Dave!
```

---

#### :information_source: キーワード引数

**キーワード引数は、引数の数や順番の変更に対して堅牢です。**例えば、`greet` メソッドに `title` 引数を追加すると、キーワードなしのメソッド呼び出しにおいて意味が変化してしまいますが、キーワードありの呼び出しでは同じままです。

```scala
def greet(title: String = "Citizen", firstName: String = "Some", lastName: String = "Body") =
  "Greetings, " + title + " " + firstName + " " + lastName + "!"

greet("Busy") // これは正しくなくなりました
// res10: String = Greetings, Busy Some Body!

greet(firstName = "Busy") // これは正しいままです
// res11: String = Greetings, Citizen Busy Body!
```

これは、多数の引数を伴うメソッドやコンストラクターを作成するとき、特に便利です。

---

### Scala の型階層

プリミティブ型とオブジェクト型を区別する Java とは異なり、Scala ではすべてがオブジェクトです。その結果、`Int` や `Boolean` のような「プリミティブ」値の型は、クラスやトレイトと同じ型階層の一部を構成しています。

![Scala 型階層](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67627/352e1090-25a8-7cc6-5ee9-f88e7c0096c0.png)

Scala は `Any` と呼ばれる最上位の基底型を持ち、その下に `AnyVal` と `AnyRef` という2つの型があります。`AnyVal` はすべての値型の基底型で、`AnyRef` はすべての参照型やクラスの基底型です。Scala や Java におけるすべてのクラスは `AnyRef` の派生型です[^value-classes]。

[^value-classes]: 実際には `AnyVal` の派生型を定義でき、それは[値クラス](http://docs.scala-lang.org/overviews/core/value-classes.html)として知られています。これらはいくつかの特殊な状況で有用なのですが、ここでは議論しません。

それらの型のいくつかは、単純に Java に存在する型の Scala における別名で、`Int` は `int`、`Boolean` は `boolean`、`AnyRef` は `java.lang.Object` です。

階層の**最下位 (bottom)** に2つの特別な型があります。`Nothing` は `throw` 式の型で、`Null` は `null` 値の型です。それらの特別な型は、他のすべての型の派生型で、コードにおける他の型を健全に保ちながらも、`throw` や `null` に型を割り当てることを補助します。下記のコードはこのことを説明しています。

```scala
def badness = throw new Exception("Error")
// badness: Nothing

def otherbadness = null
// otherbadness: Null

val bar = if(true) 123 else badness
// bar: Int = 123

val baz = if(false) "it worked" else otherbadness
// baz: String = null
```

`badness` と `otherbadness` の型はそれぞれ `Nothing` と `Null` であるにも関わらず、`bar` と `baz` の型は実用的なままです。なぜならば、`Int` は `Int` と `Nothing` の最小共通基底型で、`String` は `String` と `Null` の最小共通基底型であるからです。

### 覚えておいてほしいこと

本節では、同じ**型**を持つ様々なオブジェクトの生成を可能にする**クラス**を定義する方法を学びました。このように、クラスを使うことで、似たような性質を持つ**オブジェクトを横断的に抽象化**することができます。

クラスにおいてもオブジェクトの性質は、**フィールド**と**メソッド**の形をとります。フィールドはオブジェクトに格納される事前に計算された値で、メソッドは呼び出すことができる計算です。

クラスを宣言するための文法は、

```scala
class Name(parameter: type, ...) {
  declarationOrExpression ...
}
```

です。

`new` キーワードを使用し、コンストラクターを呼び出すことによって、クラスからオブジェクトを生成します。

また、**キーワード引数**と**デフォルト引数**についても学びました。

最後に、Scala の型階層について、Java の型階層との重複や、特殊な型である `Any` や `AnyRef`、`AnyVal`、`Nothing`、`Null`、`Unit` を含むこと、そして Java のクラスと Scala のクラスは、どちらも型階層の同じサブツリーを占有するという事実を学びました。

### 演習

今ではクラスで楽しく遊ぶために十分な機構を手に入れました。

#### 猫、再び

以前の演習に登場した猫を思い出しましょう。

| 名前 (Name)              | 色 (Colour)            | 食べ物 (Food)    |
|--------------------------|------------------------|------------------|
| オズワルド (Oswald)      | 黒 (Black)             | ミルク (Milk)    |
| ヘンダーソン (Henderson) | 茶トラ (Ginger)        | カリカリ (Chips) |
| クエンティン (Quentin)   | トラ (Tabby and white) | カレー (Curry)   |

`Cat` クラスを定義し、上の表の各猫についてオブジェクトを生成してください。

<details><summary>解答（クリックして表示）</summary><div class="solution">

これはクラスを定義する文法に慣れるための指の運動です。

```scala
class Cat(val colour: String, val food: String)

val oswald = new Cat("Black", "Milk")
val henderson = new Cat("Ginger", "Chips")
val quentin = new Cat("Tabby and white", "Curry")
```

</div></details>


#### 猫、ぶらぶらする

`willServe` メソッドを伴う `ChipShop` オブジェクトを定義してください。このメソッドは `Cat` を受け取り、猫の好きなエサがカリカリ (chips) であれば `true` を返し、そうでなければ `false` を返します。

<details><summary>解答（クリックして表示）</summary><div class="solution">

```scala

object ChipShop {
  def willServe(cat: Cat): Boolean =
    if(cat.food == "Chips")
      true
    else
      false
}
```

</div></details>


#### 監督デビュー

下記のフィールドとメソッドを伴う、`Director` と `Film` の2つのクラスを書いてください。

 - `Director` に含まれる：
    - `String` 型の `firstName` フィールド
    - `String` 型の `lastName` フィールド
    - `Int` 型の `yearOfBirth` フィールド
    - 無引数で、フルネームを返す `name` メソッド

 - `Film` に含まれる：
    - `String` 型の `name` フィールド
    - `Int` 型の `yearOfRelease` フィールド
    - `Double` 型の `imdbRating` フィールド
    - `Director` 型の `director` フィールド
    - 公開時における監督の年齢を返す `directorsAge` メソッド
    - 引数として `Director` を受け取り、`Boolean` を返す `isDirectedBy` メソッド

下記のデモデータをコードにコピー＆ペーストし、そのコードを変更せずに動作するようコンストラクターを調整してください。

```scala
val eastwood          = new Director("Clint", "Eastwood", 1930)
val mcTiernan         = new Director("John", "McTiernan", 1951)
val nolan             = new Director("Christopher", "Nolan", 1970)
val someBody          = new Director("Just", "Some Body", 1990)

val memento           = new Film("Memento", 2000, 8.5, nolan)
val darkKnight        = new Film("Dark Knight", 2008, 9.0, nolan)
val inception         = new Film("Inception", 2010, 8.8, nolan)

val highPlainsDrifter = new Film("High Plains Drifter", 1973, 7.7, eastwood)
val outlawJoseyWales  = new Film("The Outlaw Josey Wales", 1976, 7.9, eastwood)
val unforgiven        = new Film("Unforgiven", 1992, 8.3, eastwood)
val granTorino        = new Film("Gran Torino", 2008, 8.2, eastwood)
val invictus          = new Film("Invictus", 2009, 7.4, eastwood)

val predator          = new Film("Predator", 1987, 7.9, mcTiernan)
val dieHard           = new Film("Die Hard", 1988, 8.3, mcTiernan)
val huntForRedOctober = new Film("The Hunt for Red October", 1990, 7.6, mcTiernan)
val thomasCrownAffair = new Film("The Thomas Crown Affair", 1999, 6.8, mcTiernan)
```

```scala
eastwood.yearOfBirth
// res16: Int = 1930

dieHard.director.name
// res17: String = John McTiernan

invictus.isDirectedBy(nolan)
// res18: Boolean = false
```

さらに、`copy` と呼ばれる `Film` のメソッドを実装します。このメソッドは、コンストラクターと同じ引数を受け取り、映画の新しいコピーを生成します。各引数にデフォルト値を与えて、映画の値の一部を変更してコピーできるようにします。

```scala
highPlainsDrifter.copy(name = "L'homme des hautes plaines")
// res19: Film = Film(L'homme des hautes plaines,1973,7.7,Director(Clint,Eastwood,1930))

thomasCrownAffair.copy(yearOfRelease = 1968,
  director = new Director("Norman", "Jewison", 1926))
// res20: Film = Film(The Thomas Crown Affair,1968,6.8,Director(Norman,Jewison,1926))

inception.copy().copy().copy()
// res21: Film = Film(Inception,2010,8.8,Director(Christopher,Nolan,1970))
```

<details><summary>解答（クリックして表示）</summary><div class="solution">

この演習は、Scala のクラスやフィールド、メソッドの記述をいくつかのハンズオンによる体験として提供します。模範解答は下記のようになります。

```scala
class Director(
  val firstName: String,
  val lastName: String,
  val yearOfBirth: Int) {

  def name: String =
    s"$firstName $lastName"

  def copy(
    firstName: String = this.firstName,
    lastName: String = this.lastName,
    yearOfBirth: Int = this.yearOfBirth): Director =
    new Director(firstName, lastName, yearOfBirth)
}

class Film(
  val name: String,
  val yearOfRelease: Int,
  val imdbRating: Double,
  val director: Director) {

  def directorsAge =
    yearOfRelease - director.yearOfBirth

  def isDirectedBy(director: Director) =
    this.director == director

  def copy(
    name: String = this.name,
    yearOfRelease: Int = this.yearOfRelease,
    imdbRating: Double = this.imdbRating,
    director: Director = this.director): Film =
    new Film(name, yearOfRelease, imdbRating, director)
}
```

</div></details>

#### シンプル・カウンター

`Counter` クラスを実装してください。コンストラクターは `Int` を受け取るようにしてください。カウンターを増加させる `inc` メソッドとカウンターを減少させる `dec` メソッドを、それぞれ新しい `Counter` を返すように実装してください。こちらが利用例です。

```scala
new Counter(10).inc.dec.inc.inc.count
// res23: Int = 12
```

<details><summary>解答（クリックして表示）</summary><div class="solution">

```scala
class Counter(val count: Int) {
  def dec = new Counter(count - 1)
  def inc = new Counter(count + 1)
}
```

クラスやオブジェクトによる実践の傍ら、この演習には2つ目の目的があります。それは、なぜ `inc` と `dec` が、同じカウンターを直接更新する代わりに、新しい `Counter` を返すのかを考えることです。

`val` フィールドは不変なので、`count` の新しい値を伝える他の方法を思い付く必要があります。新しい `Counter` オブジェクトを返すメソッドは、代入による副作用なしに新しい状態を返す方法を与えてくれます。それはまた**メソッドチェーン (method chaining)** を可能にし、一連の更新全体をひとつの式で書くことを可能にします。

実際のところ、利用例 `new Counter(10).inc.dec.inc.inc.count` は、最後の `Int` 値を返すまでに `Counter` のインスタンスを5つ生成します。このような単純計算のための、余分なメモリと CPU のオーバーヘッドを気にするかもしれませんが、その心配はありません。JVM のような最新の実行環境では、このスタイルのプログラミングにおける余分なオーバーヘッドは、性能が最重要なコードを除き、無視して構わないものになっています。

</div></details>

#### 高速カウント

前の演習に登場した `Counter` を拡張し、ユーザーが `inc` と `dec` に `Int` 引数を任意に渡せるようにしてください。引数が省略されたときは、`1` をデフォルトにしてください。

<details><summary>解答（クリックして表示）</summary><div class="solution">

最も単純な解答はこのようになります。

```scala
class Counter(val count: Int) {
  def dec(amount: Int = 1) = new Counter(count - amount)
  def inc(amount: Int = 1) = new Counter(count + amount)
}
```

しかし、これは `inc` と `dec` に丸括弧を追加します。引数を省略したときにも、空の丸括弧を付与しなければなりません。

```scala
new Counter(10).inc
// <console>:14: error: missing argument list for method inc in class Counter
// Unapplied methods are only converted to functions when a function type is expected.
// You can make this conversion explicit by writing `inc _` or `inc(_)` instead of `inc`.
//        new Counter(10).inc
//                        ^
```

本来の丸括弧なしのメソッドを再現するには、**メソッドのオーバーロード (method overloading)** を使用して、丸括弧の付与を回避します。

```scala
class Counter(val count: Int) {
  def dec: Counter = dec()
  def inc: Counter = inc()
  def dec(amount: Int = 1): Counter = new Counter(count - amount)
  def inc(amount: Int = 1): Counter = new Counter(count + amount)
}

new Counter(10).inc.inc(10).count
// res25: Int = 21
```
</div></details>

#### 追加カウント

こちらは `Adder` と呼ばれるシンプルなクラスです。

```scala
class Adder(amount: Int) {
  def add(in: Int) = in + amount
}
```

`Counter` を拡張し、`adjust` と呼ばれるメソッドを追加してください。このメソッドは `Adder` を受け入れ、`count` に `Adder` を適用した結果を伴う新しい `Counter` を返します。

<details><summary>解答（クリックして表示）</summary><div class="solution">

```scala
class Counter(val count: Int) {
  def dec = new Counter(count - 1)
  def inc = new Counter(count + 1)
  def adjust(adder: Adder) =
    new Counter(adder.add(count))
}
```

これは興味深いパターンで、Scala の機能を学ぶにつれて、より強力になっていくでしょう。**計算を表現するために `Adder` を使用**し、それを `Counter` に受け渡しています。**メソッドは式ではない**という先の議論を思い出してください。それらはフィールドに格納したり、データとして受け渡したりできません。しかしながら、**`Adder` はオブジェクトであると同時に計算でもある**のです。

オブジェクト指向プログラミング言語において、計算としてオブジェクトを使用することは一般的なパラダイムです。例えば、Java の Swing における古典的な `ActionListener` を考えてみてください。

```java
public class MyActionListener implements ActionListener {
  public void actionPerformed(ActionEvent evt) {
    // 何か計算を実行する
  }
}
```

`Adders` や `ActionListener` のようなオブジェクトの欠点は、特定の状況での使用に限定されることです。Scala には、オブジェクトとして様々な計算の表現を可能にする、**関数 (function)** と呼ばれるより一般的な概念が含まれています。本章では、関数の背後にある概念のいくつかを紹介していきます。

</div></details>

## 関数としてのオブジェクト

前節、最後の演習で `Adder` と呼ばれるクラスを定義しました。

```scala
class Adder(amount: Int) {
  def add(in: Int): Int = in + amount
}
```

議論の中で、計算を表現するオブジェクトとしての `Adder` を説明しました。それは、値として渡すことのできるメソッドを得られたようなものでした。

計算のように振る舞うオブジェクトは強力な概念で、Scala にはそれを生成するための言語機能が完全に備わっています。それらのオブジェクトは**関数 (function)** と呼ばれ、**関数型プログラミング (functional programming)** の基礎を成します。

### apply メソッド

ここでは、関数型プログラミングをサポートする Scala の一機能**関数適用文法 (function application syntax)** を見ていきましょう。

Scala では、慣例によって、`apply` と呼ばれるメソッドを持つオブジェクトを関数のように「呼び出す」ことができます。`apply` と名付けられたメソッドによって、呼び出し文法 `foo.apply(args)` が `foo(args)` になるという特別な略記法が与えられます。

例えば、`Adder` の `add` メソッドを `apply` に改名してみましょう。

```scala
class Adder(amount: Int) {
  def apply(in: Int): Int = in + amount
}
```

```scala
val add3 = new Adder(3)
// add3: Adder = Adder@3390766f

add3.apply(2)
// res0: Int = 5

add3(4) // add3.apply(4) の略記法
// res1: Int = 7
```

この簡単なトリックによって、オブジェクトは文法的に関数らしく「見える」ようになります。オブジェクトを、変数に代入したり、引数として受け渡したり、メソッドではできなかったことがたくさんできます。

---

#### :information_source: 関数適用文法

メソッド呼び出し `object.apply(parameter, ...)` は `object(parameter, ...)` と書くこともできる。

---

### 覚えておいてほしいこと

本節では、オブジェクトを関数であるかのように「呼び出す」ための**関数適用文法**を見ました。

関数適用文法は、`apply` メソッドが定義されたどんなオブジェクトでも利用可能です。

関数適用文法によって、計算のように振る舞う第一級値（訳注：[第一級関数](https://ja.wikipedia.org/wiki/%E7%AC%AC%E4%B8%80%E7%B4%9A%E9%96%A2%E6%95%B0)）を持てるようになりました。メソッドと違って、オブジェクトはデータとして受け渡すことができます。これで、Scala における真の関数型プログラミングに一歩近付きました。

### 演習

#### 関数が関数でないのはどんなとき？

次節の最後に、いくつかのコードを書く機会があります。ここで、重要な理論上の疑問について考えてみましょう。

関数適用文法は、計算を実行する真に再利用可能なオブジェクトを生成できることに、どのくらい近付いているのでしょうか？何が足りないのでしょうか？

<details><summary>解答（クリックして表示）</summary><div class="solution">

主に足りていないものは、値を横断的に抽象化する方法である**型**です。

現時点では、数値を加算するという知識を捕捉するために `Adder` と呼ばれるクラスを定義できますが、他の開発者はその知識を使用するために、この特定のクラスについて知っている必要があるため、そのコードはコードベースを横断して可搬であるとは言えません。

`Handler` や `Callback`、`Adder`、`BinaryAdder` などのような名前で共通する関数型のライブラリを定義できますが、これはすぐに非現実的になってしまいます。

後ほど、様々な状況で使用できる一般的な関数型一式を定義することで、Scala がこの問題にどのように対処しているのかを見ていきましょう。

</div></details>

## コンパニオンオブジェクト

論理的にはクラスに所属していても、どの特定のオブジェクトからも独立しているメソッドを作成したいことがあります。そのために Java では**静的メソッド (static method)** を使用しますが、Scala にはシングルトンオブジェクトというもっと単純な解決策があります。

一般的な使用例は補助コンストラクターです。Scala がクラスについて複数のコンストラクターを定義できる文法を持つにも関わらず、Scala プログラマーはほとんどの場合、クラスと同じ名前を持つオブジェクトに、追加のコンストラクターとして `apply` メソッドを実装することを好みます。そのオブジェクトをクラスの**コンパニオンオブジェクト (companion object)** と呼びます。例えば、下記のようになります。

```scala
class Timestamp(val seconds: Long)

object Timestamp {
  def apply(hours: Int, minutes: Int, seconds: Int): Timestamp =
    new Timestamp(hours*60*60 + minutes*60 + seconds)
}
```

```scala
Timestamp(1, 1, 1).seconds
// res1: Long = 3661
```

---

#### :information_source: コンソールを有効活用する

上記の例は、`:paste` コマンドを使用することに注意してください。コンパニオンオブジェクトは、後援するクラスと同じコンパイル単位で定義されなければなりません。通常のコードベースで、これはクラスとオブジェクトを同じファイルの中に定義することを意味しますが、REPL 上では `:paste` を使用し、それらをひとつのコマンドとして入力しなければなりません。

REPL 上に `:help` と入力することでより詳細を知ることができます。

---

前述のように、Scala は、**型名 (type name)** 空間と**値名 (value name)** 空間の2つの名前空間を持ちます。このように分離することで、衝突なくクラスとコンパニオンオブジェクトに同じ名前を付けることができます。

シングルトンオブジェクトは独自の型を伴うので、**コンパニオンオブジェクトはクラスのインスタンスではない**という重要なことに注意してください。

```scala
Timestamp // 型は `Timestamp.type` であり `Timestamp` ではないことに注意
// res2: Timestamp.type = Timestamp$@74d89b8f
```

---

#### :information_source: コンパニオンオブジェクト文法

クラスのためにコンパニオンオブジェクトを定義するには、同じ名前のオブジェクトをクラスと**同じファイル**に定義します。

```scala
class Name {
  ...
}

object Name {
  ...
}
```

---

### 覚えておいてほしいこと

**コンパニオンオブジェクト**は、機能をクラスのインスタンスに関連付けることなく、クラスに関連付ける手段を提供します。一般的に、それらは追加のコンストラクターを提供するために使用されます。

コンパニオンオブジェクトは **Java の静的メソッドを代替します**。それらは等価な機能を提供し、より柔軟です。

**コンパニオンオブジェクトは、関連付けられたクラスと同じ名前を持ちます。** これは、値の名前空間と型の名前空間という2つの名前空間を Scala が持つため、名前の衝突を引き起こしません。

**コンパニオンオブジェクトは、関連付けられたクラスと同じファイルに定義されなければいけません。** REPL 上で入力するときは `:paste` モードを使用して、クラスとコンパニオンオブジェクトが同じコードブロックで入力されなければいけません。

### 演習

#### フレンドリーな人ファクトリ

姓と名を個別にではなく、姓名全体をひとつの文字列として受け取る `apply` メソッドを含む、`Person` のコンパニオンオブジェクトを実装してください。

ヒント：下記のようにして `String` を `Array` の要素に分割できます。

```scala
val parts = "John Doe".split(" ")
// parts: Array[String] = Array(John, Doe)

parts(0)
// res3: String = John
```

<details><summary>解答（クリックして表示）</summary><div class="solution">

こちらがコードです。

```scala
class Person(val firstName: String, val lastName: String) {
  def name: String =
    s"$firstName $lastName"
}

object Person {
  def apply(name: String): Person = {
    val parts = name.split(" ")
    new Person(parts(0), parts(1))
  }
}
```

こちらが使用例です。

```scala
Person.apply("John Doe").firstName // 完全なメソッド呼び出し
// res5: String = John

Person("John Doe").firstName // 糖衣適用文法
// res6: String = John
```

</div></details>

#### 業績の派生的な内容

下記のように、`Director` と `Film` のためのコンパニオンオブジェクトを記述してください。

 - `Director` コンパニオンオブジェクトに含まれる：
    - クラスのコンストラクターと同じ引数を受け取り、新しい `Director` を返す `apply` メソッド
    - 2つの `Director` を受け取り、2つのうち年齢が上の方を返す `older` メソッド

 - `Film` コンパニオンオブジェクトに含まれる：
    - クラスのコンストラクターと同じ引数を受け取り、新しい `Film` を返す `apply` メソッド
    - 2つの `Film` を受け取り、2つのうち `imdbRating` が高い方を返す `highestRating` メソッド
    - 2つの `Film` を受け取り、それぞれの撮影時に年齢が上の `Director` を返す`oldestDirectorAtTheTime` メソッド

<details><summary>解答（クリックして表示）</summary><div class="solution">

この演習は、たくさんのコードを書く練習を提供することを意図しています。前節のクラス定義を含む模範回答はこのようになります。

```scala
class Director(
  val firstName: String,
  val lastName: String,
  val yearOfBirth: Int) {

  def name: String =
    s"$firstName $lastName"

  def copy(
    firstName: String = this.firstName,
    lastName: String = this.lastName,
    yearOfBirth: Int = this.yearOfBirth) =
    new Director(firstName, lastName, yearOfBirth)
}

object Director {
  def apply(firstName: String, lastName: String, yearOfBirth: Int): Director =
    new Director(firstName, lastName, yearOfBirth)

  def older(director1: Director, director2: Director): Director =
    if (director1.yearOfBirth < director2.yearOfBirth) director1 else director2
}

class Film(
  val name: String,
  val yearOfRelease: Int,
  val imdbRating: Double,
  val director: Director) {

  def directorsAge =
    director.yearOfBirth - yearOfRelease

  def isDirectedBy(director: Director) =
    this.director == director

  def copy(
    name: String = this.name,
    yearOfRelease: Int = this.yearOfRelease,
    imdbRating: Double = this.imdbRating,
    director: Director = this.director) =
    new Film(name, yearOfRelease, imdbRating, director)
}

object Film {
  def apply(
    name: String,
    yearOfRelease: Int,
    imdbRating: Double,
    director: Director): Film =
    new Film(name, yearOfRelease, imdbRating, director)

  def newer(film1: Film, film2: Film): Film =
    if (film1.yearOfRelease < film2.yearOfRelease) film1 else film2

  def highestRating(film1: Film, film2: Film): Double = {
    val rating1 = film1.imdbRating
    val rating2 = film2.imdbRating
    if (rating1 > rating2) rating1 else rating2
  }

  def oldestDirectorAtTheTime(film1: Film, film2: Film): Director =
    if (film1.directorsAge > film2.directorsAge) film1.director else film2.director
}
```

</div></details>

#### 型か値か？

クラスとコンパニオンオブジェクトの名前付けによる類似は、新しい Scala 開発者の混乱を引き起こしがちです。コードのブロックを読んでいるとき、その部分がクラス（**型**）を示しているのか、シングルトンオブジェクト（**値**）を示しているのかについて知っていることが重要です。

**「型か値か？」** という新しくヒットしそうなクイズを思いついたので、これから試してみましょう。単語 `Film` の参照しているものが型か値かをそれぞれの事例で特定してください。

```scala
val prestige: Film = bestFilmByChristopherNolan()
```

<details><summary>解答（クリックして表示）</summary><div class="solution">
**型！**このコードは、型 `Film` の値 `prestige` を定義しています。
</div></details>

```scala
new Film("Last Action Hero", 1993, mcTiernan)
```

<details><summary>解答（クリックして表示）</summary><div class="solution">
**型！**これは、`Film` の**コンストラクター**への参照です。このコンストラクターは、**型**である**クラス** `Film` の一部です。
</div></details>

```scala
Film("Last Action Hero", 1993, mcTiernan)
```

<details><summary>解答（クリックして表示）</summary><div class="solution">
**値！**これは、下記の略記法です。

```scala
Film.apply("Last Action Hero", 1993, mcTiernan)
```

`apply` は、**シングルトンオブジェクト**（値）`Film` に定義されたメソッドです。
</div></details>

```scala
Film.newer(highPlainsDrifter, thomasCrownAffair)
```

<details><summary>解答（クリックして表示）</summary><div class="solution">
**値！**`newer` は、**シングルトンオブジェクト** `Film` に定義された別のメソッドです。
</div></details>

最後は難しいものを……

```scala
Film.type
```

<details><summary>解答（クリックして表示）</summary><div class="solution">
**値！**これは巧妙です！これを間違えたとしても許されるでしょう。

`Film.type` は、シングルトンオブジェクト `Film` の型を参照するので、この場合の `Film` は値への参照です。しかしながら、コードの断片全体は型になります。
</div></details>

## ケースクラス

**ケースクラス (case class)** は、クラスやコンパニオンオブジェクト、たくさんの実用的なデフォルト機能をまとめて定義してくれる非常に便利な略記法です。それは、やっかいごとを最小限にして、軽量なデータ保持クラスを生成するための理想的な方法になります。

ケースクラスは、クラス定義にキーワード `case` を前置するだけで生成されます。

```scala
case class Person(firstName: String, lastName: String) {
  def name = firstName + " " + lastName
}
```

ケースクラスを定義するとき、いつでも Scala は自動的に**クラスとコンパニオンオブジェクト**を生成します。

```scala
val dave = new Person("Dave", "Gurnell") // クラスを持つ
// dave: Person = Person(Dave,Gurnell)

Person // コンパニオンオブジェクトも
// res0: Person.type = Person
```

さらに、クラスとコンパニオンオブジェクトには、とても便利ないくつかの機能があらかじめ用意されています。

### ケースクラスの機能

1. **各コンストラクター引数のフィールド**。コンストラクター定義に `val` と書く必要はありませんし、明示的に書いても悪影響はありません。

    ```scala
    dave.firstName
    // res1: String = Dave
    ```

2. **デフォルト `toString` メソッド**。それは、クラスのコンストラクターに似た実用的な表現を印字します。もう `@` 記号と謎めいた16進数とはおさらばです。

    ```scala
    dave
    // res2: Person = Person(Dave,Gurnell)
    ```

3. **実用的な `equals` メソッドと `hashCode` メソッド**。それらは、オブジェクトのフィールド値に基づいて動作します。

    これは、`List` や `Set`、`Map` のようなコレクションでケースクラスを使いやすくします。また、オブジェクトを参照 ID の代わりに、それらの内容に基づいて比較できるようになります。

    ```scala
    new Person("Noel", "Welsh").equals(new Person("Noel", "Welsh"))
    // res3: Boolean = true
    
    new Person("Noel", "Welsh") == new Person("Noel", "Welsh")
    // res4: Boolean = true
    ```

4. **`copy` メソッド**。`copy` メソッドは、現在のオブジェクトと同じフィールド値を伴う新しいオブジェクトを生成します。

    ```scala
    dave.copy()
    // res5: Person = Person(Dave,Gurnell)
    ```

    `copy` メソッドは、現在のオブジェクトの代わりに、クラスの**新しいオブジェクト**を生成して返すことに注意してください。
    
    
    実のところ、`copy` メソッドは、それぞれのコンストラクター引数と一致する任意の引数を受け取ります。引数が指定されれば、新しいオブジェクトは、既存のオブジェクトに存在する値の代わりにその値を使用します。これは、1つ以上のフィールド値を変更してオブジェクトをコピーするときに、キーワード引数と一緒に使用するのが理想的です。

    ```scala
    dave.copy(firstName = "Dave2")
    // res6: Person = Person(Dave2,Gurnell)
    
    dave.copy(lastName = "Gurnell2")
    // res7: Person = Person(Dave,Gurnell2)
    ```
    
    ---
    
    #### :information_source: 値と参照の等価性
    
    Scala の `==` 演算子は Java のものとは異なります。それは、参照 ID を値として比較する代わりに `equals` に委譲します。
    
    Scala は、Java の `==` と同じ振る舞いをする `eq` と呼ばれる演算子を持ちます。しかしながら、それをアプリケーションコードで使用することはめったにありません。
    
    ```scala
    new Person("Noel", "Welsh") eq (new Person("Noel", "Welsh"))
    // res8: Boolean = false
    
    dave eq dave
    // res9: Boolean = true
    ```
    
    ---

5. ケースクラスは `java.io.Serializable` と `scala.Product` という2つのトレイトを実装します。どちらも直接使用されることはありません。後者は、ケースクラスの名前とフィールド数を調べるためのメソッドを提供します。

### ケースクラスコンパニオンオブジェクトの機能

コンパニオンオブジェクトは、クラスのコンストラクターと同じ引数を持つ `apply` メソッドを含みます。Scala プログラマーは、`new` を省略できる簡潔さのために、コンストラクターより `apply` メソッドを好む傾向があり、コンストラクターが式の中で読み易くなるようにします。

```scala
Person("Dave", "Gurnell") == Person("Noel", "Welsh")
// res10: Boolean = false

Person("Dave", "Gurnell") == Person("Dave", "Gurnell")
// res11: Boolean = true
```

最後に、コンパニオンオブジェクトは、**パターンマッチング (pattern matching)** に使用する**抽出子パターン (extractor pattern)** を実装するコードも含みます。本章で後ほど見ていきます。

---

#### :information_source: ケースクラス宣言文法

ケースクラスを宣言するための文法は、

```scala
case class Name(parameter: type, ...) {
  declarationOrExpression ...
}
```

です。ここで、

- `Name` はケースクラスの名前
- `parameter` はコンストラクター引数として与えられた名前（オプション）
- `type` はコンストラクター引数の型
- `declarationOrExpression` は宣言か式（オプション）

とします。

---

### ケースオブジェクト

最後の覚え書きです。コンストラクター引数なしでケースクラスを定義しているものをみつけたら、その代わりに**ケースオブジェクト (case object)** を定義できます。ケースオブジェクトは、単に通常のシングルトンオブジェクトのように定義されますが、もっと意味のある `toString` メソッドを持ち、`Product` と `Serializable` トレイトを継承しています。

```scala
case object Citizen {
  def firstName = "John"
  def lastName  = "Doe"
  def name = firstName + " " + lastName
}
```

```scala
Citizen.toString
// res12: String = Citizen
```

### 覚えておいてほしいこと

ケースクラスは **Scala データ型の真髄**です。それを使って、学んで、好きになってください。

ケースクラスを宣言するための文法は、`case` を付与すること以外はクラスを宣言するためのものと同じです。

```scala
case class Name(parameter: type, ...) {
  declarationOrExpression ...
}
```


ケースクラスは、タイピングの手間を省く、たくさんの自動生成されたメソッドと機能を持ちます。関連するメソッドを実装することによって、その動作を個別にオーバーライドできます。

Scala 2.10 以前は、0〜22のフィールドを含むケースクラスしか定義できませんでした。Scala 2.11 で、任意サイズのケースクラスを定義する能力を獲得しました。

### 演習

#### ケースキャット

`Cat` が色とエサを `String` で持っていることを思い出してください。`Cat` を表現するケースクラスを定義しましょう。

<details><summary>解答（クリックして表示）</summary><div class="solution">

もうひとつの簡単な指の運動です。

```scala
case class Cat(colour: String, food: String)
```

</div></details>

#### ロジャー・イーバートは言う……

> 良くない映画は長すぎるが、悪くない映画は十分に短い。

コードについては必ずしも同じことを言えませんが、この場合、`Director` と `Film` をケースクラスに変換することによって、多くの定型文を取り除くことができます。この変換を実行し、どのコードを削除できるか試してみましょう。

<details><summary>解答（クリックして表示）</summary><div class="solution">

ケースクラスは `copy` メソッドと `apply` メソッドを提供し、各コンストラクター引数の前にある `val` を記述する必要性を取り除きます。最終的なコードベースは下記のようになります。

```scala
case class Director(firstName: String, lastName: String, yearOfBirth: Int) {
  def name: String =
    s"$firstName $lastName"
}

object Director {
  def older(director1: Director, director2: Director): Director =
    if (director1.yearOfBirth < director2.yearOfBirth) director1 else director2
}

case class Film(
  name: String,
  yearOfRelease: Int,
  imdbRating: Double,
  director: Director) {

  def directorsAge =
    yearOfRelease - director.yearOfBirth

  def isDirectedBy(director: Director) =
    this.director == director
}

object Film {
  def newer(film1: Film, film2: Film): Film =
    if (film1.yearOfRelease < film2.yearOfRelease) film1 else film2

  def highestRating(film1: Film, film2: Film): Double = {
    val rating1 = film1.imdbRating
    val rating2 = film2.imdbRating
    if (rating1 > rating2) rating1 else rating2
  }

  def oldestDirectorAtTheTime(film1: Film, film2: Film): Director =
    if (film1.directorsAge > film2.directorsAge) film1.director else film2.director
}
```

このコードはかなり短くなっただけでなく、`equals` メソッドや `toString` メソッド、後ほどの演習の準備にもなるパターンマッチング機能を提供します。

</div></details>

#### ケースクラスカウンター

ケースクラスとして `Counter` を再実装し、適切なところで `copy` を使用してください。さらに、`0` をデフォルト値として `count` を初期化しましょう。

<details><summary>解答（クリックして表示）</summary><div class="solution">

```scala
case class Counter(count: Int = 0) {
  def dec = copy(count = count - 1)
  def inc = copy(count = count + 1)
}
```

これは、ほとんど引っ掛け問題です。前回の実装と、ほんの少しの違いしかありません。しかしながら、無料で得られている追加の機能に注目してください。

```scala
Counter(0) // `new` なしでオブジェクトを構築
// res16: Counter = Counter(0)

Counter().inc // 印字で `count` の値を表示
// res17: Counter = Counter(1)

Counter().inc.dec == Counter().dec.inc // 意味のある等価性を検証
// res18: Boolean = true
```

</div></details>

#### 適用、適用、適用

ケースクラスにコンパニオンオブジェクトを定義すると何が起こるでしょうか？見てみましょう。

前節の `Person` クラスを題材に、ケースクラスへの変換を試してみましょう。（ヒント：コードは上にあります。）代替 `apply` メソッドを伴うコンパニオンオブジェクトを依然として持っていることを確認してください。

<details><summary>解答（クリックして表示）</summary><div class="solution">

こちらがコードです。

```scala
case class Person(firstName: String, lastName: String) {
  def name = firstName + " " + lastName
}

object Person {
  def apply(name: String): Person = {
    val parts = name.split(" ")
    apply(parts(0), parts(1))
  }
}
```

`Person` のためにコンパニオンオブジェクトを定義しているにも関わらず、依然として Scala のケースクラスのコードは期待どおりに動作します。自動生成されたメソッドを定義したコンパニオンオブジェクトに追加するので、ひとつのコンパイル単位にクラスとコンパニオンオブジェクトを配置する必要があります。

これは、最終的に `apply` メソッドのオーバーロードによって、2つの型シグネチャをコンパニオンオブジェクトが持つこと意味します。

```scala
def apply(name: String): Person =
  // etc...

def apply(firstName: String, lastName: String): Person =
  // etc...
```

</div></details>

## パターンマッチング

これまでは、メソッドを呼び出したり、フィールドにアクセスしたりしてオブジェクトを作用させてきました。ケースクラスを使用すると、**パターンマッチング (pattern matching)** を通じた別の方法で作用させることができます。

パターンマッチングは、データの「形」に応じて式を評価できるように拡張した `if` 式のようなものです。前の例で見た `Person` ケースクラスを思い出してください。

```scala
case class Person(firstName: String, lastName: String)
```

反乱軍のメンバーを探している `Stormtrooper` を実装したいとしましょう。下記のようにパターンマッチングを使用することができます。

```scala
object Stormtrooper {
  def inspect(person: Person): String =
    person match {
      case Person("Luke", "Skywalker") => "Stop, rebel scum!"
      case Person("Han", "Solo") => "Stop, rebel scum!"
      case Person(first, last) => s"Move along, $first"
    }
}
```

パターンの文法 `Person("Luke", "Skywalker")` は、パターンマッチングするオブジェクトを構築するための文法 `Person("Luke", "Skywalker")` と一致していることに注意してください。

使用例はこちらです。

```scala
Stormtrooper.inspect(Person("Noel", "Welsh"))
// res0: String = Move along, Noel

Stormtrooper.inspect(Person("Han", "Solo"))
// res1: String = Stop, rebel scum!
```

---

#### :information_source: パターンマッチング文法

パターンマッチング式の文法は、

```scala
expr0 match {
  case pattern1 => expr1
  case pattern2 => expr2
  ...
}
```

です。ここで、

- `expr0` はパターンマッチングする値に評価される式
- `pattern1`、`pattern2` などは値に対して順番に検証されるパターンもしくは**ガード (guard)**
- `expr1`、`expr2` などは式で、最初にマッチングしたパターンの右辺式が評価される[^compilation]

とします。パターンマッチング自体は式であるため、マッチングしたパターンの右辺式の値に評価されます。
</div>

[^compilation]: 実際のところ、パターンは逐次的な検証より効率的な形にコンパイルされますが、それが意味するところは変わりません。


### パターン文法

Scala は、パターンやガードを記述するための表現力豊かな文法を持っています。ケースクラスの場合、パターン文法はコンストラクター文法と一致します。データを取り上げてみましょう。

```scala
Person("Noel", "Welsh")
// res2: Person = Person(Noel,Welsh)
```

`Person` 型に対してマッチするパターンは、下記のように記述されます。

```scala
Person(pat0, pat1)
```

ここで、`pat0` と `pat1` は、それぞれ `firstName` と `lastName` に対してマッチするパターンです。`pat0` と `pat1` の位置に使用できるパターンは4つあります。

1. 名前。それは、その位置にある任意の値にマッチし、与えられた名前に束縛されます。例えば、パターン `Person(first, last)` は、名前 `first` に値 `"Noel"` を、名前 `last` に値 `"Welsh"` を束縛します。

2. アンダースコア (`_`)。それは、任意の値にマッチし、その値を無視します。例えば、ストームトルーパーが一般市民の名についてのみ気にするのであれば、`lastName` の値を名前に束縛することを避け、単に `Person(first, _)` と書くことができます。

3. リテラル。それは、単にリテラルが表現する値に首尾よくマッチします。例えば、パターン `Person("Han", "Solo")` は、名が `"Han"` で、姓が `"Solo"` である `Person` にマッチするというわけです。

4. 同様のコンストラクタースタイル文法を使用している別のケースクラス。（訳注：ケースクラスを入れ子にできるということです。本節の演習に例があります。）

パターンマッチングでできることは他にもたくさんあり、パターンマッチングは拡張可能であることも覚えておいてください。後ほどの節でそれらの機能を見ていきます。


### 覚えておいてほしいこと

ケースクラスは、**パターンマッチング**と呼ばれる相互作用の新しい形を可能にします。パターンマッチングでは、ケースクラスによって分析し、ケースクラスに含まれる内容に応じて異なる式を評価することができます。

パターンマッチングのための文法は、

```scala
expr0 match {
  case pattern1 => expr1
  case pattern2 => expr2
  ...
}
```

です。パターンは、下記のいずれかになります。

1. 名前。その名前に任意の値が束縛される
2. アンダースコア。任意の値がマッチし、その値は無視される
3. リテラル。リテラルが意味する値にマッチする
4. ケースクラスによるコンストラクタースタイルパターン

### 演習

#### 猫にエサをあげる

`willServe` メソッドを伴う `ChipShop` オブジェクトを定義してください。このメソッドは `Cat` を受け取り、猫の好きなエサがカリカリ (chips) であれば `true` を、そうでなければ `false` を返します。パターンマッチングを使用しましょう。

<details><summary>解答（クリックして表示）</summary><div class="solution">

問題文が示唆しているスケルトンを記述することから始めましょう。

```scala
case class Cat(name: String, colour: String, food: String)
```

```scala
object ChipShop {
  def willServe(cat: Cat): Boolean =
    cat match {
      case Cat(???, ???, ???) => ???
    }
}
```

返却型は `Boolean` なので、少なくとも2つのケース、ひとつは `true`、もうひとつは `false` が必要であることがわかります。演習の文章は、それがカリカリが好きな猫とそれ以外の猫であることを示しています。これをリテラルパターンと `_` パターンで実装することができます。

```scala
object ChipShop {
  def willServe(cat: Cat): Boolean =
    cat match {
      case Cat(_, _, "Chips") => true
      case Cat(_, _, _) => false
    }
}
```

</div></details>


#### 私の芝生から出ていけ！

この演習では、映画評論家である私の父のシミュレーターを書こうと思います。それはとても単純で、クリント・イーストウッドが監督した映画はどれでも10.0、ジョン・マクティアナンが監督した映画はどれでも7.0、ほかの映画はどれでも3.0に評価されます。`Film` を受け取り、`Double` を返す `rate` メソッドを伴う `Dad` と呼ばれるオブジェクトを実装してください。パターンマッチングを使用しましょう。

<details><summary>解答（クリックして表示）</summary><div class="solution">

```scala
object Dad {
  def rate(film: Film): Double =
    film match {
      case Film(_, _, _, Director("Clint", "Eastwood", _)) => 10.0
      case Film(_, _, _, Director("John", "McTiernan", _)) => 7.0
      case _ => 3.0
    }
}
```

この場合、パターンマッチングはいささか冗長になります。後ほど、**定数パターン (constant pattern)** と呼ばれる特定の値にマッチするパターンマッチングを使用する方法を学びます。

</div></details>

## まとめ

本章では**クラス**を探求しました。そのクラスが、オブジェクトを超えた抽象化を可能にすることも見てきました。それによって、共通のプロパティを共有し、共通の型を持つオブジェクトを定義できます。

**コンパニオンオブジェクト**についてもまた見てきました。それは、Scala において、補助コンストラクターや、クラスに属さないユーティリティメソッドを定義するために使用されます。

最後に、**ケースクラス**を紹介しました。それは、ボイラープレートコードを大いに削減し、メソッド呼び出しに加えて、**パターンマッチング**というオブジェクトとの新しい相互作用の方法を可能にしました。