case class
ครั้งแรกที่ผมเห็นมัน ผมรู้สึกมึนตึบ แล้วก็สงสัยว่ามันคืออะไร
ทำไม case statement มาเกี่ยวอะไรกับ class ด้วย
หลังจากนั่งอ่าน Programming in Scala ของ Martin Odersky, Lex Spoon, Bill Venners
ก็เลยเกิดความกระจ่างขึ้น
เจ้า modifier "case" ข้างหน้า class นั้น มันทำให้ compiler ช่วย generate method บางอย่างเพิ่มขึ้นมา
เพื่อทำให้เราสามารถใช้ feature pattern matching กับ Object ได้ง่ายขึ้น
ในหนังสือเขายกตัวอย่าง กรณี expression
abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr
สมัยแรกๆที่ผมเห็น statement ข้างบน ผมก็จะนึกเดาไปว่า
ตัว case class มันต้องเขียนเรียงติดๆกันแบบเดียวกับพวก case statement
แต่จริงๆแล้วไม่ใช่ "case" เป็นแค่ modifiler (เหมือนพวก public, private, static ทำนองนั้น)
ประโยคข้างบนจริงๆแล้วก็คือ การประกาศ class ขึ้นมา 5 ตัว
ที่ดูแปลกๆ ก็คือใน scala กรณี empty body, เราไม่ต้องใส่ class body
{ }
ให้มันก็ได้ (มันจะแอบใส่ให้เราเอง)สิ่งที่ case modifier ทำก็คือ
- compiler จะช่วย generate Factory method ที่ชื่อเดียวกับ class ให้เรา
ผลก็คือ เราสามารถใช้คำสั่งแบบนี้ได้val v = Number(10)
แทนที่จะเป็น
val v = new Number(10)
ซึ่งมีประโยชน์มากเวลาที่เรา nested แบบนี้
BinOp("+", Var("x"), Number(7)) - compiler จะช่วยทำให้ argument ทุกตัวกลายเป็น read only property ของ class นั้น
เช่น
scala> val b = BinOp("+", Var("x"), Var("x"))
b: BinOp = BinOp(+,Var(x),Var(x))
scala> b.operator
res23: String = + - compiler จะช่วย generate พวก method equals, hashcode ให้เรา
โดยตัว equals method มันจะ implement ในลักษณะที่จะเปรียบเทียบทุกๆ property ของ object ให้เรา
ทำให้เราสามารถเปรียบเทียบแบบนี้ได้
scala> Var("x") == Var("x")
res21: Boolean = true
scala> BinOp("+", Number(7), Number(8)) == BinOp("+", Number(7), Number(8))
res22: Boolean = true
ดูเหมือนว่าสิ่งที่มัน gen ก็ไม่มีอะไรมาก
แต่ผลที่ได้ทำให้เราสามารถทำ pattern matching แบบนี้ได้
def simplifyTop(expr: Expr): Expr = expr match {
case UnOp("-", UnOp("-", e)) => e // Double negation
case BinOp("+", e, Number(0)) => e // Adding zero
case BinOp("*", e, Number(1)) => e // Multiplying by one
case _ => expr
}