[Java15]자바15 변경사항

Reve - 2021/07/24

자바 15버전이 2020년 9월 15일에 공개되었다.
COVID-19로 인해 재택근무가 활성화되면서 적응하느라 정신없이 보냈더니 JDK 버전이 두개나 나왔다.
1년에 두번 패치하는걸 따라잡겠다고 마음먹었는데.. 반성하는중이다.

Java15 (opens new window)
JDK15 (opens new window)

마찬가지로 자바 개발자가 알아두면 좋을 사항 몇가지만 짚어보려고 한다.


# 1. Sealed Classes (Preview)

Sealed Classes는 Client Code가 부모클래스에서 허용된 모든 자식클래스가 무엇인지 명확하게 하는 기능이다.

Sealed Classes의 목표

  • 클래스 또는 인터페이스 작성자가 상속을 제한할 수 있도록 합니다.
  • 슈퍼클래스의 사용을 제한하기 위해 접근제어자보다 더 강력한 기능을 제공합니다.
  • 패턴매칭을 이용하여 서브클래스를 명시적으로 확인할 수 있습니다.

아래와 같은 코드가 있다고 가정해보자.

public interface Shape {
    ...
}

public class Circle implements Shape {
    ...
}

public class Rectangle implements Shape {
    ...
}

public class Square implements Shape {
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Shape 변수의 구현체를 추론하는 전통적인 방식으로는 if-else구문과 instanceof를 사용하는 방식이 있다.

int getCenter(Shape shape) {
    if (shape instanceof Circle) {
        return ... ((Circle)shape).center() ...
    } else if (shape instanceof Rectangle) {
        return ... ((Rectangle)shape).length() ...
    } else if (shape instanceof Square) {
        return ... ((Square)shape).side() ...
    }
}
1
2
3
4
5
6
7
8
9

하지만 컴파일러는 위 구문만으로 Shape의 모든 구현체를 확인하고 있다는걸 알 수 없다.
따라서 위의 코드는 컴파일 에러가 발생하게 된다.

일반적으로는 if-else구문의 마지막에 else를 붙여 예외 등의 처리를 한다.
물론 구현체는 Circle, Rectangle, Square 세가지 뿐이므로 마지막 else는 실행될 수 없는 코드일 것이다.

int getCenter(Shape shape) {
    if (shape instanceof Circle) {
        return ... ((Circle)shape).center() ...
    } else if (shape instanceof Rectangle) {
        return ... ((Rectangle)shape).length() ...
    } else if (shape instanceof Square) {
        return ... ((Square)shape).side() ...
    } else {
        throw new IllegalStateException("Unknown implementation.");
    }
}
1
2
3
4
5
6
7
8
9
10
11

반대로 위와 같은 경우에 컴파일 에러가 나지 않는다고 가정해보자.
그렇다면 반대로 다른 개발자에 의해 구현체가 추가된다고 해도 컴파일 에러를 통해 인지하지 못하는 문제가 발생한다.

Sealed classes는 이러한 상황을 위해 클래스 또는 인터페이스의 자식클래스를 제한하고, 부모클래스만으로 자식클래스를 명확하게 추론할 수 있도록 해 준다.

다만, JEP-375 (opens new window) 에 의해 명확하게 추론하는 기능을 제공할 예정인데, JDK15에서는 아직 사용할 수 없다.

int getCenter(Shape shape) {
    return switch (shape) {
        case Circle c -> ... c.center() ...
        case Rectangle r -> ... r.length() ...
        case Square s -> ... s.side() ...
    };
}
1
2
3
4
5
6
7

Sealed classes의 사용 방법은 아래와 같다.

public abstract sealed class Shape permits Circle, Rectangle, Square {
    ...
}
1
2
3
public final class Circle extends Shape {
    ...
}
1
2
3
public final class Rectangle extends Shape {
    ...
}
1
2
3
public final class Square extends Shape {
    ...
}
1
2
3

모든 클래스가 같은 java파일 내에 있다면 permits를 하지 않아도 된다.

abstract sealed class Shape {
    ...
}

final class Circle extends Shape {
    ...
}

final class Rectangle extends Shape {
    ...
}

final class Square extends Shape {
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Sealed classes의 하위클래스(subclass)에는 세 가지 제약 조건이 있다.

Sealed Classes의 Subclass 제약

  1. sealed class와 subclass는 동일한 모듈에 속해야 하며, 모듈을 선언하지 않은 경우 동일한 패키지에 속해야 합니다.
  2. 모든 subclass는 sealed class를 직접 확장(extends or implements)해야 합니다.
  3. 모든 subclass는 sealed class에 의해 시작된 sealed를 계혹하는 방법을 설명하는 수정자를 선택해야 합니다.

제약 3번의 경우 사용할 수 있는 수정자는 3가지가 있다.

  1. final인 경우 계층 구조가 더이상 확장되지 않는다.
public final class Circle extends Shape {
    ...
}
1
2
3
  1. sealed인 경우 sealed class가 예상한 것 보다 더 확장될 수 있지만, 제한된 방식으로 확장된다.
public sealed class Rectangle extends Shape permits FilledRectangle, TransparentRectangle {
    ...
}
1
2
3
public final class FilledRectangle extends Rectangle {
    ...
}
1
2
3
public final class TransparentRectangle extends Rectangle {
    ...
}
1
2
3
  1. non-sealed의 경우 알려지지 않은 subclass에 의해 계층구조가 확장될 수 있다.
public non-sealed class Square extends Shape {
    ...
}
1
2
3
public class FilledSquare extends Square {
    ...
}
1
2
3

이와 마찬가지로 인터페이스와 레코드에도 sealed를 사용할 수 있다.

public sealed interface Expression permits Plus {
    ...
}
1
2
3
public final class Plus implements Expression {
    ...
}
1
2
3
public sealed interface Expression permits Plus {
    ...
}
1
2
3
public record Plus(int i, int b) implements Expression {
    ...
}
1
2
3

# 2. Hidden Classes

다른 클래스들의 bytecode에서 직접 호출될 수 없는 클래스이다.
프레임워크 또는 언어 레벨에서 동적으로 클래스를 사용하거나, 제한된 시간에만 사용되고 이후에는 필요하지 않은 클래스가 필요해서 만들어졌다. 그리고 이러한 클래스들은 일반 사용자(개발자)들에게 공개되지 않고, 접근하지 못하게 할 필요가 있다.

Hidden Class (opens new window)를 직접 만들 수는 있으나, 그 과정이 복잡하여 관련 링크만 공유한다.


# 3. ZGC (Z Garbage Collection)

Java11에서 공개된 G1GC를 대체할 ZGC가 Preview에서 Product 되었다.
64비트 운영체제만 지원이 가능하며, G1GC보다 빠르고 더 큰 메모리에 적합하다고 한다.

지원 가능한 운영체제는 아래와 같다.

  • Linux/x86_64
  • Linux/aarch64
  • MacOS (version unknown)
  • Windows10 or Server 빌드 1803 이후

-XX:+UseZGC 옵션을 이용하여 사용 가능하다.

다만 아직 default GC는 G1GC이다.

자세한 내용은 더 공부해서 별도로 포스팅을 해야겠다.


# 4. Shenandoah GC (Shenandoah garbage collector)

Java12에서 공개된 Shenandoah GC가 Experimental에서 Product 되었다.
무슨 GC가 이렇게 자꾸 나오는지 모르겠다.

이거 역시 자세한 내용은 더 공부해서 별도로 포스팅을 해야겠다.


# 5. Text Blocks

Java13에서 공개된 Text Blocks가 Preview에서 Product 되었다.
문법에 변경된 사항이 없으니 내용은 이전 포스팅 (opens new window)에서 확인하면 된다.


참고자료

Java 카테고리 내 다른 포스트