[Java] Java 17을 사용해야 하는 이유와 Java 17 변경점

Jinny·2023년 10월 23일
29

Java

목록 보기
1/2

💬 들어가며

우아한테크코스 6기 프리코스에 참여하게 되었는데, 프로그래밍 요구사항이 Java 17이었다.

처음 Java를 공부하면서 지금까지 Java 11을 썼는데
최근 시작한 팀프로젝트도 팀원이 Java 17을 써보자해서 겸사겸사 Java 17에 대해 공부하고 내용을 정리하고자 한다! 💪



☕️ 왜 Java 17을 사용해야 할까?

💡 참고:

우선 Java 17을 사용해야 하는 이유에 대해 알아보자.

Java LTS 버전의 기술 지원 기간

Java 17 이전의 LTS 버전은 Java 8Java 11이 대표적이다.
그런데 현재 서비스하고 있는 많은 레거시 프로젝트이 대부분 Java 8을 사용하고 있다.

Jetbrains에서 제공하는 통계만 봐도 Java 8의 점유율이 앞도적인 것을 확인할 수 있다.

이에 따라 현재 프로젝트들의 기술 지원을 위해 Java 8은 11보다 더 긴 지원 시간을 갖게 되었다.

  • Java 버전별 기술 지원 기간:
Java 버전지원 종료 일자
Java 8~2030.12
Java 11~2026.09
Java 17~2029.09

Java 17을 사용해야 하는 이유

Java 17을 사용하게 된 계기는 앞서 얘기한 바와 같이 개인적으로 학습이 주된 이유였다.

현업자인 제이든이 작성하신 블로그 글을 보면 현업에서는 다음과 같은 이유로 사용한다고 한다.

  1. 신규 버전을 위한 대비
    • 회사들은 Java 8의 지원 종료일이 다가옴에 따라 새로운 버전으로 마이그레이션을 준비해야 하는 상황
    • 8 → 17 이후 버전으로 바로 마이그레이션하기에는 리스크가 있으니, 17버전의 기술 적응을 완료된 상태로 마이그레션을 하면 영향이 최소화 될 것
  2. 다음 세대 플랫폼 호환을 위한 준비
    • SpringBoot 3.0부터 Java 17 이상을 지원하여 다음 세대 플랫폼 호환 준비도 주된 이유

실제로 2022년 Java 버전별 점유율을 보면 Java 8은 감소하고 Java 17은 크게 증가한 것을 확인할 수 있다.



✨ Java 17의 변경점

그럼 Java 11과 비교했을 때 Java 17의 변경점에 대해 알아보자.
참고로 해당 글에서는 모든 변경점을 다루지 않고 아래 몇가지 추가된 기능만 추려서 다룰 예정이다.

  • Text Block
  • Record
  • Sealed Classes
  • Switch Expression
  • Stream.toList()

Text Block

Java 11

Java 11 버전에서는 Json 형식의 문자열을 다음과 같이 표현해야 했다.
한눈에 봐도 가독성이 매우 안좋은 것을 확인할 수 있다.

String jsonString = "{\n" +
    "  \"name\": \"Jinny\",\n" +
    "  \"age\": 20\n" +
    "}";

Java 17

Java 17에서는 텍스트 블록을 제공해서 3개의 큰 따옴표로 랩핑해서 표현할 수 있다.

String jsonString = """
        {
          "name": "Jinny",
          "age": 20
        }
        """;

Record

Java 11

롬복 어노테이션이 편리성을 제공해주긴 하지만...
Dto 클래스를 만들 때 여러 보일러 플레이트 코드를 추가해야 했다.

public class Dto {
    
    private final int data;

    public Dto(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }
}

Java 17

Java 17에서는 Record를 활용하면 불필요한 코드를 제거할 수 있고, 클래스 자체로 명확한 의도를 표현할 수 있다.

public record Dto(
    int data
) {
}

💡 Record 특징:

  • 멤버변수는 private final로 선언된다.
  • 필드별 getter가 자동으로 생성된다.
  • equals, hashcode, toString이 자동으로 생성된다.
  • 기본생성자는 제공하지 않으므로 필요한 경우 직접 생성해야 한다.
  • final 클래스이므로 다른 클래스를 상속하거나/상속시킬 수 없다.
  • private final fields 이외의 인스턴스 필드를 선언할 수 없다.

Sealed Class

  • Sealed 클래스는 상속하거나(extends), 구현(implements) 할 클래스를 지정해두고, 해당 클래스들만 상속/구현이 가능하도록 제한하는 기능이다.
  • 이에 따라 개발자는 Sealed 클래스 코드만 봐도 어떤 클래스가 구현/상속했는지 쉽게 파악할 수 있게 되었다.
  • 또한, 의도치 않은 클래스가 상속받았을 경우, 컴파일 시점에 에러를 체크할 수 있어 의도치 않은 실수를 방지할 수 있다.
// Parent.java
public sealed class Parent permits Son, Daughter {
    ...
}


// Son.java
// permits 로 선언된 class 만 Parent class 를 상속할 수 있다.
public sealed class Son extends Parent {}

Sealed Class 특징:

  • super-class 에 sealed 키워드를 사용한다.
  • permits 키워드 뒤에 해당 클래스를 상속받을 sub-class를 선언한다.
  • sealed 된 클래스를 활용하기 위해서는 같은 모듈 혹은 같은 패키지 안에 존재 해야한다.

Switch Expression

Java 11

기존의 switch문은 다수의 casebreak가 존재하며 불필요한 중복 코드가 발생하고 있다.

public static void printDayOfWeek(String dayOfWeek) {
    switch (dayOfWeek) {
        case "Monday":
        case "Tuesday":
        case "Wednesday":
        case "Thursday":
        case "Friday":
            System.out.println("평일입니다.");
            break;
        case "Saturday":
        case "Sunday":
            System.out.println("주말입니다.");
            break;
    }

Java 17
변경된 문법에서는 똑같은 의미를 훨씬 간결하게 표현하고 있다.

public static void printDayOfWeek(String dayOfWeek) {
    switch (dayOfWeek) {
        case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" 
            -> System.out.println("평일입니다.");
        case "Saturday", "Sunday"
            -> System.out.println("주말입니다.");
    }

변경된 Switch문 문법:

  • -> 화살표를 사용해 실행문을 나타낸다.
  • 조건들을 ,를 이용해 나열할 수 있다.
  • break를 사용하지 않는다.

또한 출력문 안에서 사용이 가능해졌다.

public static void printDayOfWeek(String dayOfWeek) {
    System.out.println(
            switch (dayOfWeek) {
                case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> System.out.println("평일입니다.");
                case "Saturday", "Sunday" -> System.out.println("주말입니다.");
                default -> "디폴트 값입니다.";
            }
        );
    }
    

Stream.toList()

기존에 Collectors.toList().toList()로 표현할 수 있게 되었다.

Java 11

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

List<Integer> result = numbers.stream()
        .filter(number -> number > 1)
        .collect(Collectors.toList());

Java 17

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

List<Integer> result = numbers.stream()
        .filter(number -> number > 1)
        .toList(); // 변경
System.out.println(result);

🔍 그렇다면 두 메서드는 완전 동일한 것으로 볼 수 있을까?
JavaDocs를 살펴보면 두 메서드의 return type이 다르다.

Collectors.toList()
: ArrayList를 리턴하고 있으며, 불변 타입이 아니다.

toList()
:불변 List를 리턴한다.

🔍 그렇다면 toList()는 Collectors.toUnmodifiableList() 과 동일할까?

  • JavaDocs에 따르면 Collectors.toUnmodifiableList()는 내부에서 null 체크를 하고 있지만, toList()는 null 체크를 하지 않아 null 값이 들어갈 수 있다.

The returned Collector disallows null values and will throw NullPointerException if it is presented with a null value.

번역: 반환된 Collector는 null 값을 허용하지 않으며, null 값을 전달받으면 NullPointerException을 throw합니다.

Collectors.toUnmodifiableList()


🔗 참고 자료:

profile
공부는 마라톤이다. 한꺼번에 많은 것을 하다 지치지 말고 조금씩, 꾸준히, 자주하자.

5개의 댓글

comment-user-thumbnail
2023년 10월 24일

좋은 글 감사합니다!

1개의 답글
comment-user-thumbnail
2023년 10월 24일

최고에여! 정리해주셔서 감사합니다

1개의 답글
comment-user-thumbnail
2023년 11월 5일

좋은 글 감사합니다

Collectors.toList() , toList() 의 차이점에 대해서는 생각해보지 못했는데 생각할 기회가 되었네요

답글 달기