티스토리 뷰

Java

JDK 14 Release note

LichKing 2020. 2. 7. 16:35

2020년 3월 17일 GA 예정인 JDK 14 의 몇가지 피처들을 정리해본다.

 

Pattern Matching for instanceof (Preview)

런타임에 객체의 타입을 확인하는 instanceof 연산자는 보통 아래와같은 관용구(idiom)로 많이 사용된다.

if(obj instanceof String) {
  String s = (String) obj;
}

이 instanceof 연산자를 확장하여 아래와같은 문법을 지원한다.

if(obj instanceof String s) {
  // String 타입의 s 변수 사용
}

코틀린에 대한 경험이 있는 사람이라면 바로 느꼈겠지만 코틀린의 스마트 캐스팅과 유사한 문법으로 보인다. 다만 한가지 좀 이해하기 어려운 스펙이 있는데

if (obj instanceof String s){                 
    System.out.println(s.startsWith("h"));  
} else {                                     
    System.out.println(s);                  
}                                           

if 문이 false 인 경우에도 else 블럭에서 s 에 대한 이용이 가능하다. 다만 이 경우에는 obj가 String 타입이 아니므로 obj 를 캐스트해서 사용하는게 아니라 해당 클래스의 필드를 참조하게된다.

public class First {
    private String s = "hello";

    public void test() {
        Object o = 123;

        if (o instanceof String s){
            System.out.println(s);
        } else {
            System.out.println(s);
        }
    }
}

이 경우 else 블럭에서 s 는 인스턴스 필드 s 를 참조하게되며 인스턴스 필드에 s 가 존재하지않는 경우 컴파일 되지않는다. 인스턴스 필드 s 가 존재하지않더라도 else 블럭에서 s 를 이용하지않는다면 컴파일은 된다. 메서드가 static 일경우 인스턴스 필드 s 를 참조할 수 없으므로 static 필드 s 가 존재해야한다. 14에서도 프리뷰인 스펙이라 이 스펙이 끝까지 유지되지않을 수도 있다.

 

Helpful NullpointerExceptions

a.i = 99;

이런 코드에서 NPE 가 발생할 경우 아래와같은 에러메세지를 볼 수 있다.

Exception in thread "main" java.lang.NullPointerException
    at Prog.main(Prog.java:5)

에러메세지에는 파일명, 메서드, 문제의 코드라인을 보여주는데 위와같은 예제코드에서는 이 메세지로 어디가 문제인지를 찾을 수 있다. a 가 null 이라는것 말이다.

a.b.c.i = 99;
a[i][j][k] = 99;

하지만 이런 경우엔 문제의 라인을 안다고하더라도 어디가 null 인지 코드와 메세지만 보고서는 찾기가 어렵다(코드 자체도 썩 좋은코드로 보이지는 않지만).

a.i = b.j;
x().y().i = 99;

이런 경우에도 코드만 보고선 문제를 찾기 어렵다. "a가 null일까? b가 null일까?", "x()가 null일까 y()가 null일까?"

이런 에러메세지가 아래와 같이 좀 더 세분화되게된다.

Exception in thread "main" java.lang.NullPointerException: 
        Cannot assign field "i" because "a" is null
    at Prog.main(Prog.java:5)
Exception in thread "main" java.lang.NullPointerException: 
        Cannot read field "c" because "a.b" is null
    at Prog.main(Prog.java:5)
Exception in thread "main" java.lang.NullPointerException:
        Cannot load from object array because "a[i][j]" is null
    at Prog.main(Prog.java:5)
Exception in thread "main" java.lang.NullPointerException:
        Cannot read field "j" because "b" is null
    at Prog.main(Prog.java:5)

 

 

Records (Preview)

자바에 대해 불만을 얘기하는 사람들이 항상 주장하는 것들중 하나는 자바의 문법이 너무 장황하고 보일러플레이트 코드들이 많이 필요하다는 것이다. 자바보다 더 나은 경험을 제공하겠다며 나온 언어(대표적으로 코틀린)들이 가장 먼저 자바보다 낫다고 주장하는것들도 이부분이다. 객체가 아닌 단순 자료구조성 데이터 클래스도 자바에서는 별도의 문법이 없기때문에 클래스선언, 필드선언, 제어자(getter)를 정의해줘야하는것은 물론이고, toString(), hashcode(), equals() 와 같은 메서드들도 오버라이딩 해주어야한다. 그나마 롬복이라는 걸출한 라이브러리가 이를 해결해주고있긴하다.

 

이를 대체하기위해 record 라는걸 도입한다. record 는 코틀린의 data class 와 비슷하다는 생각이 들게되는데 위에서 얘기한 보일러플레이트를 상당히 줄여준다(하지만 이 기능의 목표가 자바 내 전반적인 보일러플레이트를 제거하는건 아니라고한다.).

record Person(String name, int age) {}

record 는 이런식으로 정의한다(kotlin 의 data class 와 유사하다.).

Person person1 = new Person("LichKing", 32);
System.out.println(person1);                
                                            
Person person2 = new Person("LichKing", 32);
System.out.println(person1.equals(person2));

클래스를 이용해 인스턴스를 생성하는 것과 마찬가지의 방법으로 사용할 수 있으며 toString() 이나 equals() 가 기본 오버라이딩 되어있다. 불변이고, 자료구조성 클래스이기때문에 인스턴스필드에 접근제어자를 붙일 수 없으며, 내부 데이터를 변경할 수 없다.

record Person(String name, int age) { 
    public boolean isMale() {         
        return true;                  
    }                                 
}                                     

이런 형태로 내부에 메서드를 정의할 수도 있다. 추가로 클래스를 상속받을 순 없으며 인터페이스 구현만 가능하다.

 

Switch Expressions (Standard)

기존 switch 문에서 다중 조건을 적용하기위해서는 아래와같이 해야했다.

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}

하지만 이런코드는 기존 작성자가 이유가 있어서 break 를 뺀건지 실수로 누락한건지 의도를 알기 힘들었다(개인적으로 이런 switch 문을 매우 좋아하지않는다. 그래서 다중조건은 if 를 이용한다.).

 

그랬던 switch 문을 아래와같이 개선할 수 있게된다.

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

그리고 switch 문이 Statement 에서 Expression 으로 바뀐다. 그래서 이런식으로 리턴값을 받을 수도 있다.

public static void main(String[] args) {
    E e = E.A;                          
    int i = switch(e) {                 
        case A,B -> 1;                  
        case C -> 2;                    
    };                                  
                                        
    System.out.println(i);              
}                                       
                                        
enum E {                                
    A, B, C                             
}                                       

상당히 코틀린스러워졌다. 당연하게도 switch 내에서 System.out.println() 과 같이 void 메서드를 호출하면 리턴값이 없으므로 위 표현식은 사용할 수 없다. 그리고 모든 리턴값은 리턴값을 받는 타입에 의해 정의된다. 무슨말인가 하면 리턴값을 받는 변수의 타입이 Object 이면 switch expression 내에서 다양한 타입을 리턴해도 된다는 의미다.

public static void main(String[] args) { 
    E e = E.A;                           
    Object o = switch(e) {               
        case A,B -> 1;                   
        case C -> "1";                   
    };                                   
                                         
    System.out.println(o);               
}                                        
                                         
enum E {                                 
    A, B, C                              
}                                        

이런식으로 작성할 수 있다.

 

그런데 switch 에서 매칭됐을때 메서드를 하나 실행하고 값을 리턴하고싶은 경우도 있을 수 있다. 가령 이런 경우다.

int i = switch(e) {                       
    case B -> 1;                          
    default -> {                          
        // 출력문을 실행한 후 5를 리턴하고싶다.          
        System.out.println("hello");      
        5;                                
    }                                     
};                                        

당장 생각나는건 return 을 이용하는건데 switch 내에서 return 을 사용하면 switch 가 종료되는게 아니라 해당 메서드가 종료되어버린다. 이런 경우에 사용하기위해 yield 키워드가 추가됐다.

int i = switch(e) {                    
    case B -> 1;                       
    default -> {                       
        // 출력문을 실행한 후 5를 리턴하고싶다.       
        System.out.println("hello");   
        yield 5;                       
    }                                  
};                                     

 

Remove the Concurrent Mark Sweep (CMS)

CMS GC 가 삭제됐다. 이와함께 ZCG 의 윈도우, 맥 지원이 릴리즈노트에 포함된거보면 G1GC, ZGC 를 이용하라는 의미인것 같다.

 

Text Blocks (Second Preview)

멀티라인 문자열이 포함됐다. 많은 분들이 정식 릴리즈하기를 기다리고있는 내용이지 않을까 싶다.

String sql = """                  
            SELECT * FROM DUAL;   
        """;                      
                                  
System.out.println(sql);          

이런게 가능해진다.

 

참고: https://openjdk.java.net/projects/jdk/14/

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함