-
Notifications
You must be signed in to change notification settings - Fork 36
15장 case classes and pattern matching
kingori edited this page Nov 19, 2011
·
12 revisions
패턴매칭할 클래스에 case키워드를 사용한다.
간단한 예제로 알아보자.
scala> abstract class Expr
defined class Expr
scala> case class Var(name: String) extends Expr
defined class Var
scala> case class Number(num: Double) extends Expr
defined class Number
scala> case class UnOp(operator: String, arg: Expr) extends Expr
defined class UnOp
scala> case class BinOp(operator: String,
| left: Expr, right: Expr) extends Expr
defined class BinOp
case classes modifier가 주는 문법적 편리함
- 클래스명으로 팩토리메소드 추가할 수 있다.
- case 클래스의 파라미터 리스트에 있는 인자들은 모두 val
- toString, hashCode, equals 메소드의 "natural"구현 사용할 수 있다.
- copy 메소드를 사용할 수 있다.
- 패턴매칭을 지원한다.
- pattern matching
- selector match { alternatives }
- switch와 다른 점
- match도 expression이다. 즉, 값을 반환한다.
- 명시적으로 break; 를 해야 하는 switch와 달리 match를 찾은 이후엔 계속 matching을 시도하지 않는다.
- 어떤 패턴에도 매칭되지 않으면 MatchError 예외를 던진다. 예외를 피하려면
case _ =>
를 명시하는 방법을 써야 한다.
- 패턴 유형
- wildcard pattern:
case _ =>
. 아무거나 다 대응됨. java switch의 default와 비슷. - constant pattern: 특정 값. 리터럴/ val / 싱글턴 객체 사용 가능함.
- wildcard pattern:
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
- variable pattern: 아무 객체에나 매칭. _와 달리 매칭된 variable을 활용할 수 있음
expr match {
case 0 => "zero"
case somethingElse => "not zero: "+ somethingElse
}
* variable pattern과 constant를 구분하기 위해 소문자로 시작하는 이름은 pattern variable, 나머지는 상수로 취급.
* 소문자 상수를 어쩔 수 없이 써야한다면? (그러나 이런 짓 하지 말자)
* prefix with qualifier ex) Math.pi
* back tick 사용 ex) \`pi\`
import math.{E,Pi}
val pi = math.Pi
E match {
case Pi => "strange math? Pi = "+ Pi //상수 매칭
case _ => "OK"
}
res0: java.lang.String = OK
E match {
case pi => "strange math? Pi = "+ pi //변수 매칭
//case _ => "OK" //syntax error: unreachable code
}
res1: java.lang.String = strange math? Pi = 2.718281828459045
- constructor pattern: 생성자명(patterns)
- pattern 안에 pattern을 쓸 수 있음: scala는 deep match를 지원함.
expr match {
case BinOp("+", e, Number(0)) => println("a deep match")
case _ =>
}
- sequence pattern: List나 Array 류의 sequence type에 매칭.
expr match {
case List(0, _, _) => println("found it")
case _ =>
}
expr match {
case List(0, _*) => println("found it")
case _ =>
}
- tuple pattern: tuple에 매칭.
expr match {
case (a, b, c) => println("matched "+ a + b + c)
case _ =>
}
- typed pattern: 타입에 매칭
def generalSize(x: Any) = x match {
case s: String => s.length //matches every non-null(!) instance of String. caution: as type of x is Any and type of s is String, you should use s to access member of String.
case m: Map[_, _] => m.size
case _ => 1
}
cf) poor java style. yuck!
if (x.isInstanceOf[String]) {
val s = x.asInstanceOf[String]
s.length
} else ...
* 자바의 generic과 마찬가지로 type erasing이 존재함. 따라서 collection 내부의 type matching 등은 불가능함. 단, array는 예외임.
scala> def isIntIntMap(x: Any) = x match { //does not work as expected
case m: Map[Int, Int] => true
case _ => false
}
warning: there were unchecked warnings; rerun with unchecked for details
isIntIntMap: (x: Any)Boolean
extractor는 chapter.26에서 만날 수 있습니다.