9. 4.1.1 トレイトの例
case class Anonymous(
id: String,
createdAt: Date = new Date()
) extends Visitor
case class User(
id: String,
email: String,
createdAt: Date = new Date()
) extends Visitor
21. 4.2 あれかこれかで他にはない:シールドトレイト
シールドトレイトはトレイト宣言に sealed と記述するだけで作成できます。
sealed trait Visitor {
def id: String
def createdAt: Date
def age: Long = new Date().getTime() - createdAt.getTime()
}
case class User(id: String, email: String,
createdAt: Date = new Date()) extends Visitor
case class Anonymous(id: String,
createdAt: Date = new Date()) extends Visitor
37. 4.4.2 不足しているパターン
has-a or パターンは「A は B か C を持つ」を意味します。これを実装するには2つの方
法があります。
「A は D を持ち、D は B か C である」と言えます。これを実装するために2つのパターン
を機械的に適用することができます。
trait A {
def d: D
}
sealed trait D
final case class B() extends D
final case class C() extends D
38. 4.4.2 不足しているパターン
あるいは、「A は D か E である、D は B を持ち、E は C を持つ」と言えます。これも直
接的にコードに変換できます。
sealed trait A
final case class D(b: B) extends A
final case class E(c: C) extends A
39. 4.4.3 キーポイント:トレイトによるデータモデリング
直積型による has-a and パターンと直和型による is-a or パターンで、データを機械的
に Scala コードを変換するのを見ました。このタイプのデータは代数的データ型として知
られています。それらのパターンを理解することは理想的な Scala コードを書く上でとて
も重要です。
43. 4.5.1 ポリモーフィズムを使用した構造的再帰
val anA: A = B()
// anA: A = B()
anA.foo
// res1: String = It's B!
val anA: A = C()
// anA: A = C()
anA.foo
// res2: String = It's C!
44.
45. 4.5.1 ポリモーフィズムを使用した構造的再帰
トレイトで実装を定義することもでき、派生クラスでは override キーワードを使用して実
装を変更します。
sealed trait A {
def foo: String = "It's A!"
}
final case class B() extends A {
override def foo: String = "It's B!"
}
final case class C() extends A {
override def foo: String = "It's C!"
}
46. 4.5.1 ポリモーフィズムを使用した構造的再帰
val anA: A = B()
// anA: A = B()
anA.foo
// res1: String = It's B!
トレイトでデフォルト実装を提供する場合、すべての派生型で妥当な実装であることを確
実にすべきということを覚えておいてください。
47.
48. ノート:直積型ポリモーフィズムパターン
A が B 型の b と C 型の c を持ち、F 型を返すメソッド f を記述したい場合、シンプルに
いつもの方法でメソッドを記述する。
case class A(b: B, c: C) {
def f: F = ???
}
メソッドの本体で、F 型の結果を構築するために、 b と c とメソッド引数を必ず使用しな
ければならない。
48
49. ノート:直和型ポリモーフィズムパターン
A が B か C であり、F 型を返すメソッド f を記述したい場合、A で f を抽象メソッドとして
定義し、B と C で具象実装を提供する。
sealed trait A {
def f: F
}
final case class B() extends A {
def f: F = ???
}
final case class C() extends A {
def f: F = ???
}
49
51. ノート:直積型パターンマッチパターン
A が B 型の b と C 型の c を持ち、A を受け入れ F を返すメソッド f を記述したい場合、
下記のように記述する。
def f(a: A): F = a match {
case A(b, c) => ???
}
メソッドの本体で、F 型の結果を構築するために、 b と c を使用する。
51
52. ノート:直和型パターンマッチパターン
A が B か C であり、A を受け入れ F を返すメソッド f を記述したい場合、B と C をパ
ターンマッチのケースとして定義する。
def f(a: A): F = a match {
case B() => ???
case C() => ???
}
52
53. 4.5.3 完全な例:共通
sealed trait Food
final case object Antelope extends Food
final case object TigerFood extends Food
final case object Licorice extends Food
final case class CatFood(food: String) extends Food
54.
55. 4.5.3 完全な例:ポリモーフィズム
sealed trait Feline {
def dinner: Food
}
final case class Lion() extends Feline {
def dinner: Food = Antelope
}
final case class Tiger() extends Feline {
def dinner: Food = TigerFood
}
final case class Panther() extends Feline {
def dinner: Food = Licorice
}
final case class Cat(favouriteFood: String) extends Feline {
def dinner: Food = CatFood(favouriteFood)
}
56.
57. 4.5.3 完全な例:パターンマッチ(基底トレイト)
sealed trait Feline {
def dinner: Food = this match {
case Lion() => Antelope
case Tiger() => TigerFood
case Panther() => Licorice
case Cat(favouriteFood) => CatFood(favouriteFood)
}
}
final case class Lion() extends Feline
final case class Tiger() extends Feline
final case class Panther() extends Feline
final case class Cat(favouriteFood: String) extends Feline
58.
59. 4.5.3 完全な例:パターンマッチ(別オブジェクト)
sealed trait Feline
final case class Lion() extends Feline
final case class Tiger() extends Feline
final case class Panther() extends Feline
final case class Cat(favouriteFood: String) extends Feline
object Diner {
def dinner(feline: Feline): Food = feline match {
case Lion() => Antelope
case Tiger() => TigerFood
case Panther() => Licorice
case Cat(food) => CatFood(food)
}
}
70. 4.6 再帰的データ
End の解答は 0 と決定できます。Pair は Int 型を返す必要があり、tl について再帰呼
び出しをする必要があります。
def sum(list: IntList): Int = list match {
case End => 0
case Pair(hd, tl) => ??? sum(tl)
}
79. 4.6.2 末尾再帰
import scala.annotation.tailrec
@tailrec
def sum(list: IntList): Int = list match {
case End => 0
case Pair(hd, tl) => hd + sum(tl)
}
// <console>:18: error: could not optimize @tailrec annotated method sum: it
...
// def sum(list: IntList): Int = list match {
// ^
@tailrec
def sum(list: IntList, total: Int = 0): Int = list match {
case End => total
case Pair(hd, tl) => sum(tl, total + hd)
}
// sum: (list: IntList, total: Int)Int