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); |
이런게 가능해진다.
'Web > Java' 카테고리의 다른 글
[Java] Timeleaf 타임리프 (0) | 2020.06.10 |
---|---|
[JAVA] hasNext() 와 next() 의 차이 (0) | 2020.06.09 |
[정의] CI / DI (1) | 2020.05.11 |
[Java] 비동기처리 방법 (0) | 2020.05.08 |
Collections emptyMap()을 사용하는 경우 (0) | 2020.05.07 |