본문 바로가기

Web/Java

[Java] JDK14 기능정리

반응형

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://okky.kr/article/676912

반응형

'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