Software Workshop

역사

목표

if (obj instanceof String) {
    String s = (String) obj;    // grr...
    ...
}

패턴 매칭이란

instanceof 연산자 확장

if (obj instanceof String s) {
    // Let pattern matching do the work!
    ...
}

패턴 변수의 범위

패턴 변수가 분명히 일치한 곳에서 사용 가능함(in scope)

if (a instanceof Point p) {
    // p is in scope
    ...
}
// p not in scope here
if (b instanceof Point p) {     // Sure!
    ...
}

if block 뿐만 아니라 &&로 연결된 조건문의 rhs에서도 사용 가능함

if (obj instanceof String s && s.length() > 5) {
    flag = s.contains("jdk");
}

||로 연결된 조건문의 rhs에서는 사용 가능하지 않음

if (obj instanceof String s || s.length() > 5) {    // Error!
    ...
}

또 다른 사례

public void onlyForStrings(Object o) throws MyException {
    if (!(o instanceof String s))
        throw new MyException();
    // s is in scope
    System.out.println(s);
    ...
}

지역 변수의 한 가지 - 실습 1

class Example1 {
    String s;

    void test1(Object o) {
        if (o instanceof String s) {
            System.out.println(s);      // Field s is shadowed
            s = s + "\n";               // Assignment to pattern variable
            ...
        }
        System.out.println(s);          // Refers to field s
        ...
    }
}

변수 shadowing에 주의할 것

class Example2 {
    Point p;

    void test2(Object o) {
        if (o instanceof Point p) {
            // p refers to the pattern variable
            ...
        } else {
            // p refers to the field
            ...
        }
    }
}

리팩토링 사례

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);
    };

다형성에 대한 응용

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

참고자료