파란하늘의 지식창고
Published 2021. 3. 18. 13:44
JDK 16 New Features Study/Java
반응형

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

Spec

Java SE 16 Platform JSR 391에 정의된 바와 같이 JSR 391 구현이 목표

실제 Spec은 Final Release Specification 문서를 참고해야 함

Final Release Specification Feature Summary

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

Component Feature
infrastructure Migrate from Mercurial to Git
infrastructure Migrate to GitHub
hotspot / gc ZGC: Concurrent Thread-Stack Processing
hotspot / runtime Elastic Metaspace
tools / jpackage Packaging Tool
core-libs Foreign-Memory Access API (Third Incubator)
specification / language Pattern Matching for instanceof
specification / language Records
specification / language Sealed Classes (Second Preview)

 

JEP 357: Migrate from Mercurial to Git

버전 관리 시스템이 Mercurial에서 git으로 변경되었다고 한다.

jdk/jdk의 경우 Mercurial에서 .hg 디렉토리의 경우 1.2GB이고 git에서 .git 디렉토리의 경우 300MB정도 된다고 한다.

JEP 369: Migrate to GitHub

소스코드 저장소를 github으로 변경하였다.

기존의 경우 메일링 리스트를 통해 협업하고 JBS (JDK Bug System)을 통해 버그 보고서를 제출하였는데 github이 제공하는 기능들과 많은 github 유저들의 접근성이 확보되면서 이후 OpenJDK 기여자의 workflow가 어떻게 변화할지 관심이 간다.

JEP 376: ZGC: Concurrent Thread-Stack Processing

ZGC의 thread-stack processing이 safepoint 에서 concurrent phase로 이동하였다.

JEP 387: Elastic Metaspace

사용하지 않는 HotSpot class-metadata (즉 metaspace) memory를 운영체제에 보다 신속하게 반환하고 metatspace footprint를 줄이고 metaspace code를 단순화하여 유지 관리 비용을 줄였다.

JEP 392: Packaging Tool

자체 포함된 java application을 package하기 위한 jpackage tool이다.

JDK 14에서 incubating tool로 소개되었던 jpackage toole이 JDK 15를 거쳐 추가된 피드백을 반영하여 JDK 16에서 정식 기능으로 승격되었다.

따라서 기존의 jdk.incubator.jpackage에서 jdk.jpackage로 변경되었다.

JEP 393: Foreign-Memory Access API (Third Incubator)

Java application이 Java heap 외부의 외부 메모리에 안전하고 효율적으로 액세스 할 수 있도록하는 API를 제공한다.

JEP 394: Pattern Matching for instanceof

instanceof 연산자에 대한 pattern matching으로 Java 프로그래밍 언어를 향상시킵니다.

pattern matching을 사용하면 프로그램의 공통 논리, 즉 object에서 component의 조건부 추출을보다 간결하고 안전하게 표현할 수 있습니다.

JDK 14에서 preview feature로 제안되었고 JDK 15를 거쳐 JDK 16에서 최종 확정 되었다.

JDK 16에서는 다음과 같이 개선되었다.

  • pattern variable이 암묵적으로 final이라는 제한을 해제하여 local variable과 pattern variable간의 비대칭성을 줄인다.
  • pattern instanceof 표현식이 S type의 표현식을 T 유형의 pattern과 비교하는 것을 compile-time error로 처리한다.
    여기서 S는 T의 subtype이다. (이 instanceof 표현식은 항상 성공하기 때문에 무의미하고 반대의 경우 이미 compile-time error이다.)

기존에 instanceof를 다음처럼 사용하였다.

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

String instance인 것을 확인한 후 String으로 casting 하는 과정이 필요했다.

이는 다음과 같이 사용할수 있게 된다.

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

다만 이렇게 부여된 s의 경우 해당 flow scoping 내에서만 유효하다.

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

만약 조건문이 and 인 경우는 다음처럼 사용이 가능하다.

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

단순히 block 내에서 유효한게 아닌 flow scoping 내에서 유효하다고 한 이유는 위와 같이 block 내가 아닌 조건문의 다음 조건 절에서도 사용을 하기 위한 범위이기 때문이다.

or 조건의 경우 선행 조건이 맞지 않은 상태에서는 s의 값이 부여되지 않으므로 후행 조건을 처리할 수 없다.

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

하지만 instanceof의 pattern matching 사용은 명시적인 casting 수를 크게줄여준다.

public boolean equals(Object o) {
    return (o instanceof CaseInsensitiveString) &&
        ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

기존에 위와 같이 사용하던 경우 instanceof의 pattern matching을 사용하면 아래와 같이 간결해진다.

public boolean equals(Object o) {
    return (o instanceof CaseInsensitiveString cis) &&
        cis.s.equalsIgnoreCase(s);
}

다른 경우를 보면

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

(TODO 이 설명 이후 pattern variable의 flow scoping에 대한 내용은 이후 테스트해보고 적을 예정..)

JEP 395: Records

Record에 대해서는 JDK 15에 소개한 글을 참고하면 된다.

2020.10.13 - [Study/Java] - JDK 15 New Features

JDK 16에서는 아래 내용들이 추가로 기술되었다.

Constructors for record classes

암묵적인 formal parameter의 유효성을 검사하는 compact canonical constructor는 다음과 같다.

record Range(int lo, int hi) {
    Range {
        if (lo > hi)  // referring here to the implicit constructor parameters
            throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
    }
}

다음은 formal parameter를 정규화하는 compact canonical constructor이다.

record Rational(int num, int denom) {
    Rational {
        int gcd = gcd(num, denom);
        num /= gcd;
        denom /= gcd;
    }
}

이 선언은 일반적인 constructor 형식과 동일하다.

record Rational(int num, int denom) {
    Rational(int num, int demon) {
        // Normalization
        int gcd = gcd(num, denom);
        num /= gcd;
        denom /= gcd;
        // Initialization
        this.num = num;
        this.denom = denom;
    }
}

Compatibility and migration

abstract class java.lang.Record는 모든 record class의 공통 super class이다.

모든 java source file은 암묵적으로 java.lang.Record를 가져온다.

그러나 applciation이 다른 pakcage에서 Record란 이름의 class를 import하면 compiler error가 발생할 수 있다.

다음과 같이 com.myapp.Record가 있는 경우

package com.myapp;

public class Record {
    public String greeting;
    public Record(String greeting) {
        this.greeting = greeting;
    }
}

다음 예제는 wildcard를 사용하여 com.myapp.Record를 import하지만 compile하지 않는다.

package org.example;
import com.myapp.*;

public class MyappPackageExample {
    public static void main(String[] args) {
       Record r = new Record("Hello world!");
    }
}

compiler는 아래와 유사한 error message를 생성한다.

./org/example/MyappPackageExample.java:6: error: reference to Record is ambiguous
       Record r = new Record("Hello world!");
       ^
  both class com.myapp.Record in com.myapp and class java.lang.Record in java.lang match

./org/example/MyappPackageExample.java:6: error: reference to Record is ambiguous
       Record r = new Record("Hello world!");
                      ^
  both class com.myapp.Record in com.myapp and class java.lang.Record in java.lang match

com.myapp package와 java.lang package 두군데에서 wildcard로 인해 Record를 가져온다.

결과적으로 두 class 모두 우선순위를 갖지 않으며 complier는 단순 이름 사용을 발견하면 error message를 생성한다.

이 예제를 compile 하려면 import 구문을 변경하여 Record의 완전한 이름을 가져오도록 한다.

import com.myapp.Record;

JEP 397: Sealed Classes (Second Preview)

Sealed Classes에 대해서는 JDK 15에 소개한 글을 참고하면 된다.

2020.10.13 - [Study/Java] - JDK 15 New Features

 

반응형
profile

파란하늘의 지식창고

@Bluesky_

내용이 유익했다면 광고 배너를 클릭 해주세요