Java

Java 23

창욱씨 2026. 5. 6. 18:34
반응형

1. 📝 Markdown Documentation Comments (JEP 467) - 정식 출시

Javadoc 주석을 HTML 태그 없이 Markdown 문법으로 작성할 수 있습니다. /// 으로 시작하는 새로운 주석 스타일을 사용합니다.

// ❌ 기존 HTML 방식
/**
 * <h2>두 수를 더합니다.</h2>
 * <p>두 정수를 받아 합계를 반환합니다.</p>
 * <ul>
 *   <li>{@code a} - 첫 번째 정수</li>
 *   <li>{@code b} - 두 번째 정수</li>
 * </ul>
 * @return 두 수의 합
 */
public int add(int a, int b) {
    return a + b;
}

// ✅ Java 23 Markdown 방식
/// ## 두 수를 더합니다.
///
/// 두 정수를 받아 합계를 반환합니다.
///
/// - `a` - 첫 번째 정수
/// - `b` - 두 번째 정수
///
/// @return 두 수의 합
public int add(int a, int b) {
    return a + b;
}

2. 🔢 Primitive Types in Patterns (JEP 455 - Preview)

기존 패턴 매칭(instanceof, switch)은 참조 타입만 지원했지만, Java 23부터 int, doubleprimitive 타입도 패턴 매칭에 사용할 수 있습니다.

// ✅ instanceof에서 primitive 타입 패턴 매칭
Object obj = 42;

// 기존 방식
if (obj instanceof Integer i && i > 10) {
    System.out.println("10보다 큰 정수: " + i);
}

// Java 23 - primitive 직접 사용
if (obj instanceof int i && i > 10) {
    System.out.println("10보다 큰 int: " + i);
}

// ✅ switch에서 primitive 타입 패턴 매칭
double value = 3.14;

String result = switch (value) {
    case double d when d < 0   -> "음수";
    case double d when d == 0  -> "영";
    case double d when d < 1   -> "0과 1 사이";
    default                    -> "1 이상";
};
System.out.println(result); // 출력: 1 이상

// ✅ 숫자 범위에 따른 분기 처리
int score = 85;

String grade = switch (score) {
    case int s when s >= 90 -> "A";
    case int s when s >= 80 -> "B";
    case int s when s >= 70 -> "C";
    default                 -> "F";
};
System.out.println(grade); // 출력: B

3. 🔄 Stream Gatherers (JEP 473 - 2nd Preview)

커스텀 중간 스트림 연산을 직접 정의할 수 있습니다. 기존의 고정된 filter, map 등의 연산 외에 원하는 중간 처리 로직을 자유롭게 만들 수 있습니다.

import java.util.stream.Gatherers;

// ✅ 내장 Gatherer - 고정 크기 윈도우
Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.windowFixed(2))
    .forEach(System.out::println);
// 출력: [1, 2], [3, 4], [5]

// ✅ 내장 Gatherer - 슬라이딩 윈도우
Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.windowSliding(3))
    .forEach(System.out::println);
// 출력: [1, 2, 3], [2, 3, 4], [3, 4, 5]

// ✅ 내장 Gatherer - 누적 집계 (scan)
Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.scan(() -> 0, Integer::sum))
    .forEach(System.out::println);
// 출력: 1, 3, 6, 10, 15

// ✅ 커스텀 Gatherer - 중복 제거 후 처음 N개만 추출
Gatherer<Integer, ?, Integer> distinctLimit = Gatherer.ofSequential(
    () -> new HashSet<Integer>(),
    (seen, element, downstream) -> {
        if (seen.add(element)) {          // 중복이 아닌 경우만
            return downstream.push(element); // 다음 단계로 전달
        }
        return true;
    }
);

Stream.of(1, 2, 2, 3, 1, 4, 3, 5)
    .gather(distinctLimit)
    .forEach(System.out::println);
// 출력: 1, 2, 3, 4, 5

4. 🚀 Implicitly Declared Classes & Instance Main Methods (JEP 477 - 3rd Preview)

Java 입문자의 진입장벽을 낮추기 위해 클래스 선언과 복잡한 main 메서드 시그니처를 생략할 수 있습니다.

// ❌ 기존 방식
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

// ✅ Java 23 방식 - 클래스, static, args 모두 생략 가능
void main() {
    System.out.println("Hello, World!");
}

// ✅ 간단한 계산기 예제
void main() {
    int a = 10, b = 20;
    System.out.println("합계: " + (a + b));       // 합계: 30
    System.out.println("곱셈: " + (a * b));       // 곱셈: 200
    System.out.println("평균: " + (a + b) / 2.0); // 평균: 15.0
}

5. 🧵 Structured Concurrency (JEP 480 - 3rd Preview)

여러 비동기 작업의 생명주기를 하나의 블록에서 구조적으로 관리합니다. 작업 실패 시 자동으로 나머지 작업을 취소하고 리소스를 정리합니다.

// ✅ ShutdownOnFailure - 하나라도 실패하면 나머지 모두 취소
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> user  = scope.fork(() -> fetchUser());   // 병렬 실행
    Future<String> order = scope.fork(() -> fetchOrder());  // 병렬 실행

    scope.join();           // 두 작업 모두 완료될 때까지 대기
    scope.throwIfFailed();  // 실패한 작업이 있으면 예외 전파

    return new Result(user.resultNow(), order.resultNow());
}
// try 블록을 벗어나면 모든 하위 스레드 자동 정리

// ✅ ShutdownOnSuccess - 가장 먼저 성공한 결과 사용 (경쟁 실행)
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
    scope.fork(() -> fetchFromServerA()); // 서버 A에서 조회
    scope.fork(() -> fetchFromServerB()); // 서버 B에서 조회 (더 빠른 쪽 사용)

    scope.join();
    return scope.result(); // 먼저 성공한 결과 반환, 나머지는 자동 취소
}

6. 🔒 Scoped Values (JEP 481 - 3rd Preview)

ThreadLocal의 문제점(메모리 누수, 변경 가능성)을 해결하는 불변 데이터 공유 메커니즘입니다. 특정 스코프 내에서만 유효한 값을 안전하게 전달합니다.

// ✅ ScopedValue 선언
static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();

// 요청 처리 진입점
void handleRequest(User user, String requestId) {
    // 스코프 내에서만 값 바인딩 (여러 값 동시 바인딩 가능)
    ScopedValue.where(CURRENT_USER, user)
               .where(REQUEST_ID, requestId)
               .run(() -> {
                   authenticate(); // CURRENT_USER 접근 가능
                   logRequest();   // REQUEST_ID 접근 가능
                   processData();  // 둘 다 접근 가능
               });
    // 블록을 벗어나면 자동 해제 - 메모리 누수 없음
}

void authenticate() {
    User user = CURRENT_USER.get();
    System.out.println("인증 확인: " + user.name());
}

void logRequest() {
    String id = REQUEST_ID.get();
    System.out.println("요청 ID 로깅: " + id);
}
항목 ThreadLocal ScopedValue
값 변경 ✅ 가능 ❌ 불변
유효 범위 스레드 전체 생존 기간 명시적 블록 내부
메모리 누수 위험 ⚠️ 있음 ✅ 없음
Virtual Thread 성능 ❌ 비효율 ✅ 최적화

7. 🏗️ Flexible Constructor Bodies (JEP 482 - 2nd Preview)

기존에는 생성자에서 super() 또는 this() 호출이 반드시 첫 번째 줄에 있어야 했습니다. Java 23부터는 super() 호출 이전에도 인스턴스 필드에 영향을 주지 않는 코드를 작성할 수 있습니다.

class Animal {
    String name;
    Animal(String name) {
        this.name = name;
    }
}

class Dog extends Animal {

    // ❌ 기존 방식 - super() 전에 유효성 검사 불가
    Dog(String name) {
        super(name); // 반드시 첫 줄이어야 함
        if (name == null || name.isBlank()) { // super() 이후에만 검사 가능
            throw new IllegalArgumentException("이름은 비어있을 수 없습니다.");
        }
    }

    // ✅ Java 23 방식 - super() 호출 전에 유효성 검사 가능
    Dog(String name) {
        // super() 전에 검증 로직 작성 가능
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("이름은 비어있을 수 없습니다.");
        }
        super(name); // 검증 후 부모 생성자 호출
    }
}

// ✅ 데이터 전처리 후 super() 호출
class Circle extends Shape {
    Circle(double radius) {
        // super() 전에 값 가공 가능
        double normalized = Math.abs(radius); // 음수 반지름 보정
        super(normalized);
    }
}
반응형

'Java' 카테고리의 다른 글

Java에서 데이터 없음을 표현하는 방법  (0) 2026.04.18
Synchronized & ReentrantLock  (0) 2025.07.15
ThreadLocal  (0) 2020.05.26
Java 예외 처리  (0) 2020.04.25
.map()과 .flatMap()의 차이  (0) 2020.04.10