우아한테크코스 6기 프리코스에 참여하게 되었는데, 프로그래밍 요구사항이 Java 17이었다.
처음 Java를 공부하면서 지금까지 Java 11을 썼는데
최근 시작한 팀프로젝트도 팀원이 Java 17을 써보자해서 겸사겸사 Java 17에 대해 공부하고 내용을 정리하고자 한다! 💪
💡 참고:
Java 17은 2021년 9월에 공개된 LTS 버전으로 Oracle JDK 기준 2029년 9월까지 지원된다.- 공식 문서: https://openjdk.org/projects/jdk/17
우선 Java 17을 사용해야 하는 이유에 대해 알아보자.
Java 17 이전의 LTS 버전은 Java 8과 Java 11이 대표적이다.
그런데 현재 서비스하고 있는 많은 레거시 프로젝트이 대부분 Java 8을 사용하고 있다.
Jetbrains에서 제공하는 통계만 봐도 Java 8의 점유율이 앞도적인 것을 확인할 수 있다.
이에 따라 현재 프로젝트들의 기술 지원을 위해 Java 8은 11보다 더 긴 지원 시간을 갖게 되었다.
| Java 버전 | 지원 종료 일자 |
|---|---|
| Java 8 | ~2030.12 |
| Java 11 | ~2026.09 |
| Java 17 | ~2029.09 |
Java 17을 사용하게 된 계기는 앞서 얘기한 바와 같이 개인적으로 학습이 주된 이유였다.
현업자인 제이든이 작성하신 블로그 글을 보면 현업에서는 다음과 같은 이유로 사용한다고 한다.
- 신규 버전을 위한 대비
- 회사들은
Java 8의 지원 종료일이 다가옴에 따라 새로운 버전으로 마이그레이션을 준비해야 하는 상황- 8 → 17 이후 버전으로 바로 마이그레이션하기에는 리스크가 있으니, 17버전의 기술 적응을 완료된 상태로 마이그레션을 하면 영향이 최소화 될 것
- 다음 세대 플랫폼 호환을 위한 준비
SpringBoot 3.0부터Java 17이상을 지원하여 다음 세대 플랫폼 호환 준비도 주된 이유
실제로 2022년 Java 버전별 점유율을 보면 Java 8은 감소하고 Java 17은 크게 증가한 것을 확인할 수 있다.
그럼 Java 11과 비교했을 때 Java 17의 변경점에 대해 알아보자.
참고로 해당 글에서는 모든 변경점을 다루지 않고 아래 몇가지 추가된 기능만 추려서 다룰 예정이다.
- Text Block
- Record
- Sealed Classes
- Switch Expression
- Stream.toList()
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
}
""";
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 클래스는 상속하거나(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된 클래스를 활용하기 위해서는 같은 모듈 혹은 같은 패키지 안에 존재 해야한다.
Java 11
기존의 switch문은 다수의 case와 break가 존재하며 불필요한 중복 코드가 발생하고 있다.
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 -> "디폴트 값입니다.";
}
);
}
기존에 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()
좋은 글 감사합니다!