파란하늘의 지식창고
Published 2023. 5. 7. 15:39
JDK 20 New Features Study/Java
반응형

JDK의 버전별 변경 사항은 이곳을 참고하세요.


Spec

Java SE 20 Platform JSR 395에 정의된 바와 같이 JSR 395 구현이 목표
실제 Spec은 Final Release Specification 문서를 참고해야 함

Final Release Specification Feature Summary

전체 JEP Feature 목록은 OpenJDK의 JDK20 문서로 확인할 수 있다.

JEP Component Feature
JEP 429 core-libs Scoped Values (Incubator)
JEP 432 specification/language Record Patterns (Second Preview)
JEP 433 specification/language Pattern Matching for switch (Fourth Preview)
JEP 434 core-libs Foreign Function & Memory API (Second Preview)
JEP 436 core-libs Virtual Threads (Second Preview)
JEP 437 core-libs Structured Concurrency (Second Incubator)
JEP 438 core-libs Vector API (Fifth Incubator)

JEP 429: Scoped Values (Incubator)

범위가 지정된 값을 도입하여 thread 내부 및 thread 간에 변경 불가능한 데이터(immutable data)를 공유할 수 있다.
특히 많은 수의 virtual thread를 사용할 때  thread local variable 보다 선호된다.
Scoped Value를 사용하면 method argument에 의존하지 않고 component 간 데이터를 안전하고 효율적으로 공유할 수 있다.
일반적으로 final static field로 선언되므로 많은 component에서 쉽게 접근할 수 있다.
thread local variable과 마찬가지로 Scoped Value에는 thread당 하나씩 여러개의 구현이 있다.
thread local variable과 달리 Scoped Value는 한 번 작성된 다음 변경할 수 없으며 thread 실행 중 제한된 기간 동안만 사용할 수 있다.
아래와 같이 Scoped Value를 사용할 수 있다.
범위가 지정된 값과 bind할 Object를 나타내는 ScopedValue.where(...) 를 호출한다.
run(...) 호출은 Scoped Value를 binding하여 현재 thrmead에 대한 특정한 구체화를 제공한 다음 argument로 전달된 lambda expression을 실행한다.
run(...) 호출의 lifetime 동안 lambda expression 또는 해당 expressiond에서 직접 또는 간접적으로 호출된 모든 method는 value의 get() method를 통해 scoped value를 읽을 수 있다.
run(...) method가 완료되면 binding은 destroy 된다.
final static ScopedValue<...> V = new ScopedValue<>();

// In some method 
ScopedValue.where(V, <value>) 
           .run(() -> { ... V.get() ... call methods ... });

// In a method called directly or indirectly from the lambda expression 
... V.get() ...

JEP 432: Record Patterns (Second Preview)

JDK 19의 JEP 405로 첫 번째 Preview가 제안되었었다.
이 주요 변경 사항은 다음과 같다.
  • generic record pattern의 type argument 추론에 대한 지원 추가
  • 향상된 for 문의 header에 표시할 record pattern에 대한 지원을 추가
  • named record pattern에 대한 지원을 제거
추가된 부분에 대해서만 정리하였다.
기존 내용에 nested pattern이 일치하지 않는 경우에 대해 설명을 추가하였다.
아래의 경우 record Pair는 Object로 x, y argument를 선언하고 실제 사용에선 int값을 사용하였다.
이에 대해 instanceof 사용 시 두 개의 nested pattern이 일치하지 않으므로 else 구문이 실행되게 된다.
record Pair(Object x, Object y) {} 
Pair p = new Pair(42, 42); 
if (p instanceof Pair(String s, String t)) { 
    System.out.println(s + ", " + t); 
} else { 
    System.out.println("Not a pair of strings"); 
}
향상된 for문에서 record pattenr을 허용하면 record value의 collection을 쉽게 반복하고 각 record의 구성 요소를 빠르게 추출할 수 있다.
record Point(int x, int y) {}

static void dump(Point[] pointArray) { 
    for (Point(var x, var y) : pointArray) {        // Record Pattern in header! 
        System.out.println("(" + x + ", " + y + ")"); 
    } 
}

향상된 for문의 record pattern은 nested pattern을 가질 수 있다. 예를 들면 다음과 같다.

enum Color { RED, GREEN, BLUE } 
record ColoredPoint(Point p, Color c) {} 
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

static void printUpperLeftColors(Rectangle[] r) { 
    for (Rectangle(ColoredPoint(Point p, Color c), ColoredPoint lr): r) { 
         System.out.println(c); 
    } 
}

Record patterns and enhanced for statements

R이 record pattern인 경우 향상된 for 문의 형식

for (R : e) S
위는 record pattern이 없는 다음 향상된 for 문과 동일하다.
for (var tmp : e) { 
    switch(tmp) { 
        case null -> throw new MatchException(new NullPointerException()); 
        case R -> S; 
    } 
}
  • record pattern R은 array 또는 Iterable의 element type에 적용 가능해야(applicable) 한다.
  • record pattern R은 array 또는 Iterable의 element type에 철저해야(exhaustive) 한다.
  • e의 element에 null이 있는 경우 향상된 for문을 실행하면 MatchException이 발생한다.

예를 들면 다음과 같다.

record Pair(Object fst, Object snd){}

static void notApplicable(String[] arg) { 
    for (Pair(var fst, var snd): arg) {   // Compile-time error, pattern not applicable 
        System.out.println("An element"); 
    } 
}

static void notExhaustive(Pair[] arg) { 
    for (Pair(String s, String t): arg) { // Compile-time error, pattern not exhaustive 
        System.out.println(s+", "+t); 
    } 
}

static void exceptionTest() { 
    Pair[] ps = new Pair[]{  
        new Pair(1,2),  
        null,  
        new Pair("hello","world") 
    }; 
    for (Pair(var f, var s): ps) {  // Run-time MatchException 
        System.out.println(f); 
    } 
}

JEP 433: Pattern Matching for switch (Fourth Preview)

JDK 17의 JEP406, JDK 18의 JEP 420, JDK 19의 JEP 427로 제안되었었다.
이번 주요 변경 사항은 다음과 같다.
 
  • run time에 적용되는 switch label이 없는 경우 enum class에 대한 전체 switch (즉, switch expression 또는 pattern switch 문)가 IncompatibleClassChangeError가 아닌 MatchException을 throw 함
  • switch label의 문법이 더 단순해짐
  • generic record pattern에 대한 type argument 추론은 이제 pattern을 지원하는 다른 구성과 함께 switch expression 및 명령문에서 지원됨
 
추가된 부분에 대해서만 정리하였다.
읽으려는 switch block의 switch label에 대한 문법을 수정한다.
SwitchLabel: 
  case CaseConstant { , CaseConstant } 
  case null [, default] 
  case Pattern 
  default

1c. record pattern의 type argument 추론

record pattern이 generic record class의 name을 지정하지만 type argument를 제공하지 않는 경우 (즉, record pattern이 raw type을 사용하는 경우) type argument는 항상 유추된다.
예를 들면 다음과 같다.

 

record MyPair<S,T>(S fst, T snd){};

static void recordInference(MyPair<String, Integer> pair){ 
    switch (pair) { 
        case MyPair(var f, var s) ->  
            ... // Inferred record Pattern MyPair<String,Integer>(var f, var s) 
        ... 
    } 
}
record pattern에 대한 type argument 추론은 pattern을 지원하는 모든 구문 (switch 문 및 expression, instanceof expression, 향상된 for 문)에서 지원된다.
 
5. Errors
pattern matching이 갑자기 완료될 수 있다.
예를 들어 값을 record pattern과 일치 시킬 때 record의 accessor method가 갑자기 완료될 수 있다.
이 경우 pattern matching은 MatchException을 throw하여 갑자기 완료되도록 정의된다.
이러한 pattern이 switch에 label로 나타나면 MatchException을 던저 switch가 갑자기 완료된다.
pattern을 when expression으로 보호하고 expression이 갑자기 완료될 때를 평가하는 경우 동일한 이유로 switch가 갑자기 완료된다.
pattern switch의 label이 selector expression의 값과 일치하지 않으면 pattern switch가 완전해야 하므로 MatchException을 throw하여 switch가 갑자기 완료된다.
예를 들면 다음과 같다.

 

record R(int i){ 
    public int i(){      // accessor method for i 
        return i / 0; 
    } 
}

static void exampleAnR(R r) { 
    switch(r) { 
        case R(var i): System.out.println(i); 
    } 
}
exampleAnR(new R(42)) 로 인해 MatchException의 throw가 발생한다.
대조적으로

 

static void example(Object obj) { 
    switch (obj) { 
        case R r when (r.i / 0 == 1): System.out.println("It's an R!"); 
        default: break; 
    } 
}
example(new R(42))로 인해 ArithmeticException의 throw가 발생한다.
 
pattern switch semantic에 맞추기 위해 이제 실행 시 적용되는 switch label이 없을 때 enum class에 대한 switch expression이 IncompatibleClassChangeError가 아닌 MatchException을 throw한다.
language에 대한 minor incompatible 변경 사항이다.

 

JEP 434: Foreign Function & Memory API (Second Preview)

JDK 19의 JEP 424로 제안 되었었다.
이번 주요 변경 사항은 다음과 같다.
  • MemorySegment 및 MemoryAddress 추상화가 통합되었다. (Memory address는 이제 zero-length인 memory segment로 모델링 된다.)
  • switch expression 및 statement(JEP 433)에서 pattern matching을 쉽게 사용할 수 있도록 sealed MemoryLayout hierarchy가 향상되었다.
  • maintenance boundaries를 넘어 segment를 쉽게 공유할 수 있도록 MemorySession이 Arena 및 SegmentScope로 분할되었다.
반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요