역사
- JEP 305 in Java 14
- JEP 375 in Java 15
- JEP 394 in Java 16
목표
- 어떤 객체가 특정 타입인지의 여부를 확인하는 코드는 자주 사용됨
- 특정 타입인지 확인하고
- 캐스팅하고
- 그 값을 새로운 변수에 정의해야 함
instanceof-and-cast idiom
if (obj instanceof String) {
String s = (String) obj;
...
}
패턴 매칭이란
- 어떤 객체의 형태가 간결하게
패턴으로 표현되도록 하고, 입력에 대해 그 형태가 매칭되는지 확인하는 것
- 문자열 패턴매칭: 정규표현식 패턴매칭이나 globbing 패턴 매칭
- 객체 타입 패턴매칭
- 패턴
- predicate or test: 대상에 적용할 수 있는 조건
- 지역 변수의 집합: 조건이 만족되었을 때 대상으로부터 추출 가능한 패턴 변수
타입 패턴
- 타입을 명세하는 predicate
단일 패턴 변수
instanceof 연산자 확장
- instanceof 연산자가 특정 타입 하나가 아니라 타입 패턴에 대응되도록 확장됨
if (obj instanceof String s) {
...
}
- 객체가 특정 타입과 일치하면 캐스팅과 변수 선언 및 초기화까지 처리됨
패턴 변수의 범위
패턴 변수가 분명히 일치한 곳에서 사용 가능함(in scope)
if (a instanceof Point p) {
...
}
if (b instanceof Point p) {
...
}
if block 뿐만 아니라 &&로 연결된 조건문의 rhs에서도 사용 가능함
if (obj instanceof String s && s.length() > 5) {
flag = s.contains("jdk");
}
||로 연결된 조건문의 rhs에서는 사용 가능하지 않음
if (obj instanceof String s || s.length() > 5) {
...
}
또 다른 사례
public void onlyForStrings(Object o) throws MyException {
if (!(o instanceof String s))
throw new MyException();
System.out.println(s);
...
}
지역 변수의 한 가지 - 실습 1
class Example1 {
String s;
void test1(Object o) {
if (o instanceof String s) {
System.out.println(s);
s = s + "\n";
...
}
System.out.println(s);
...
}
}
변수 shadowing에 주의할 것
class Example2 {
Point p;
void test2(Object o) {
if (o instanceof Point p) {
...
} else {
...
}
}
}
리팩토링 사례
1
public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString) &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString cis) &&
cis.s.equalsIgnoreCase(s);
}
2
public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
Point other = (Point) o;
return x == other.x
&& y == other.y;
}
public boolean equals(Object o) {
return (o instanceof Point other)
&& x == other.x
&& y == other.y;
}
JEP 406: Pattern Matching for switch 맛보기
switch-case - 실습 2
String formatted = "unknown";
if (obj instanceof Integer) {
int i = (Integer) obj;
formatted = String.format("int %d", i);
} else if (obj instanceof Byte) {
byte b = (Byte) obj;
formatted = String.format("byte %d", b);
} else if (obj instanceof Long) {
long l = (Long) obj;
formatted = String.format("long %d", l);
} else if (obj instanceof Double) {
double d = (Double) obj;
formatted = String.format("double %f", d);
} else if (obj instanceof String) {
String s = (String) obj;
formatted = String.format("String %s", s);
}
String formatted =
switch (obj) {
case Integer i -> String.format("int %d", i);
case Byte b -> String.format("byte %d", b);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s, s);
default -> String.format("Object %s", obj);
};
다형성에 대한 응용
- 현재는 향후 계획으로 잡혀 있는 기능이라서 현재 버전의 JDK와 IntelliJ에서는 지원되지 않음
interface Node {
int eval();
}
class IntNode implements Node {
int value;
int eval() { return value; }
}
class NegNode implements Node {
Node node;
int eval() { return -node.eval(); }
}
class MulNode implements Node {
Node left, right;
int eval() { return left.eval() * right.eval(); }
}
class AddNode implements Node {
Node left, right;
int eval() { return left.eval() + right.eval(); }
}
int eval(Node n) {
return switch(n) {
case IntNode(int i) -> i;
case NegNode(Node n) -> -eval(n);
case AddNode(Node left, Node right) -> eval(left) + eval(right);
case MulNode(Node left, Node right) -> eval(left) * eval(right);
};
}
예제코드
patternmatch.zip
참고자료