ケースクラスとパターンマッチ

1. case class(ケースクラス)

プロパティを持ったクラスを簡単に宣言する事ができます。以下の例では、社員(Emplyoyee)は、名前とIDと誕生日の属性を持っています。

case class Employee(name: String, id:Long, birthday:Date)

ケースクラスは、new せずにインスタンス化できたり、普通に属性に値を取得・設定(var の場合のみ)できたり、比較が簡単にできたりします。

val employee1 = Employee("一郎",1,new java.util.Date(1976,12,1))
println("名前は" + employee1.name)
// 名前は一郎
println(employee1.toString)
// Employee(一郎,1,Mon Jan 01 00:00:00 JST 3877)
val employee9 = Employee("一郎",1,new java.util.Date(1976,12,1))
if(employee1 == employee9) println("同じです") else println("異なります")
// 同じです
if(employee1.equals(employee9)) println("同じです") else println("異なります")
// 同じです

単純な case class の定義から、コンパイラが便利なメソッドを生成してくれています。

2. pattern match(パターンマッチ)

Scala には match 式と呼ばれる、条件分岐の構文があります。

val employee10 = "花子"
employee10 match {
  case "花子" => println("花子さんです。")
  case _ => println("不明な社員です。")
}
//  花子さんです。

Smatch の前の変数が、ブロック({と})の中の case XXX のXXXに一致する場合に、 => 以降の処理を実行します。 上記例では、『println(“花子さんです。”)』が実行されています。 ちなみに _ (アンダースコア)は”どれにも一致しない場合のデフォルト値”の扱いになります。 この match { case xxx } は色々なマッチングが可能です。

// 簡易なクラス
class MyFour(val value:Int)

// マッチングさせる型も色々
def desc(x:Any) = x match {
  case 1 => "これは数字の 1です"
  case "2" => "これは文字の 2です"
  case "Three" => "これは文字列の3です"
  case y:MyFour => "これはクラスの" + y.value + "です"
  case _ => "不明な変数です"
}

desc (new MyFour(4))
// "これはクラスの4です"
desc ("五")
// 不明な変数です

このように数値型(1)、文字型(”2”、”Three”)、クラス(MyFour)などの様々なパターンでマッチングが可能です。 また、if文で条件指定が可能です。このように条件指定することをpattern guard(パターンガード)と呼びます。

def descIf(x:Any) = x match {
  case x1:Int if x1 <= 10 => "10以下です"
  case x2:Int if x2 <= 100 => "100以下です"
  case _ => "不明な変数です。" + x
}

3.自動で宣言されるメソッド

パターンマッチから、再度ケースクラスのお話です。 “case class” と通常の “class”の違いはnew が必要/不要の違いのみではありません。 Scala のコンパイラが自動で生成してくれるメソッドがあります。

// case class
case class Employee(var name: String, id:Long, birthday:Date)
// 通常のclass
class EmployeeClass(var name: String, var id:Long, var birthday:Date)

// ケースクラスと通常クラスの比較
object EmpCompare{

  val eCase = Employee("一郎",1,new java.util.Date(1976,12,1))
  val eCase2 = Employee("一郎",1,new java.util.Date(1976,12,1))
  val eClass = new EmployeeClass("一郎",1,new java.util.Date(1976,12,1))
  val eClass2 = new EmployeeClass("一郎",1,new java.util.Date(1976,12,1))

  def comp = {
    println("case class" + eCase.toString())
    println("class" + eClass.toString())
  }

  def compEqual = {
    println("case class:" + (eCase == eCase2))
    println("class:" + (eClass == eClass2))
  }
}

// comp 関数を呼び出した場合
EmpCompare.comp
// case class:Employee(一郎,1,Mon Jan 01 00:00:00 JST 3877)
// class:chapter15caseclass.EmployeeClass@5056dfcb
// compEqual 関数を呼び出した場合
EmpCompare.compEqual
// case class:true
// class:false

“case class”の場合、toString は意味のある文字列になっていますが、通常のクラスはこのようにクラス名称の情報が出力されるだけです。 また、オブジェクト同士の比較もプロパティごとに行ってくれるため、同じ値が各プロパティに入っていた場合にはtrueが返されます。

def patternCase(x: Employee) = x match {
  case Employee("一郎", y1, y2) => println("一郎の社員番号:" + y1 + "/誕生日:" + y2)
  case Employee("次郎", y1, y2) => println("次郎の社員番号:" + y1 + "/誕生日:" + y2)
  case Employee("三郎", y1, y2) => println("三郎の社員番号:" + y1 + "/誕生日:" + y2)
  case _ => println("不明な社員")
}

patternCase(Employee("一郎",1,new java.util.Date(1988,8,8)))
// 一郎の社員番号:1/誕生日:Sat Sep 08 00:00:00 JST 3888

このようにパターンマッチとケースクラスを利用して簡単にマッチングが可能です。