java 9 ~ 16 + 17 특징 정리
이번에 spring boot 3.0 / spring 6 에선 자바 17을 default 로 사용한다고 합니다.
그런 김에 새로운 자바의 feature 들을 정리해보았습니다
출처
자바 9~16
https://www.youtube.com/watch?v=7SlDdzVk6GE&t=61s
자바 17
https://youtu.be/GJB-RyHKHjY%EF%BB%BF
읽어보기
- 여기어때 jdk 도입 이유
- java support 기간 및 java 8 support 중단
- spring boot 3.0 전환 고려
많이 쓸것 같은 부분 * 으로 표시
Jdk 9
인터페이스 private 메서드 추가 *
자바8에 추가된 인터페이스 defalult 메서드와 static 메서드 안에서 쓰일 수 있도록 인터페이스에 private 인터페이스 추가
public interface Foo {
static void buzz() {
System.out.print("Hello");
staticBaz();
}
private static void staticBaz() {
System.out.println(" static world!");
}
}
try-with-resouce + 실질적인 final 변수 *
기존에는 try () 괄호 안에서만 선언이 가능했는데 이젠 밖에서 선언하는 게 가능
@Test
void testTryWithResource() throws IOException {
BufferedReader br = Files.newBufferedReader(Paths.get("a.txt"));
try (br) {
String line = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
콜렉션 팩토리 메서드 (of) **
콜렉션들의 생성이 간편해짐
@Test
void testCollection() {
List<String> list = List.of("hi");
Map<String, String> map = Map.of("a", "aa");
Set<Integer> set = Set.of(1, 2, 3);
}
Arrays *
compare
mismatch
@Test
void testArrays() {
List<String> a = List.of("hi");
List<String> b = List.of("hi");
int comp = Arrays.compare(a.toArray(new String[0]), b.toArray(new String[0]));
int firstIdx = Arrays.mismatch(a.toArray(new String[0]), b.toArray(new String[0]));
System.out.println(comp);
System.out.println(firstIdx);
}
Jdk 10
var (로컬 변수 타입 추론) **
var a = 10;
@Test
void testTypeReferences() {
var a = 10;
var result = new ArrayList<Integer>();
// jdk 11 부터는 람다식에 사용 가능
}
Jdk11
String 메서드 추가 *
- isBlank();
- lines();
- repeat();
- strip();
- stripLeading();
- stripTrailing();
@Test
void testString() {
String line = "line\\nline\\n";
// System.out.println(line.isBlank());
Stream<String> lines = line.lines();
// Stream<String> lines = Arrays.stream(line.split("\\n"));
System.out.println("lines test");
lines.forEach(System.out::println);
String line20 = line.repeat(10);
System.out.println(line20);
String lineStrip = " hello ";
System.out.println(lineStrip.strip());
System.out.println(lineStrip.stripLeading());
System.out.println(lineStrip.stripTrailing());
}
strip() vs stripLeading(); vs stripTrailing();
Files 메서드 *
- writeString
- readString
@Test
void testFiles() throws IOException {
Files.writeString(Path.of("a.txt"), "string", StandardOpenOption.CREATE);
String str = Files.readString(Path.of("a.txt"));
System.out.println(str);
}
Jdk 12
String indent 함수 추가
문자열 앞에 whitespace 추가
@Test
void testStringJdk12() {
String a = "abc\\ndef".indent(3);
System.out.println(a);
}
String transform 함수 추가
변환할 때 formatter 바로 적용 가능
@Test
void testDateUtil() {
var a = "20221121".transform(this::toLocalDate);
System.out.println(a);
}
private LocalDate toLocalDate(String dateInString) {
return LocalDate.parse(dateInString, DateTimeFormatter.BASIC_ISO_DATE);
}
Jdk 14
Switch 문 개선
값 직전 반환 추가 **
yield 예약어 이용한 리턴 방식 추가
@Test
void testSwitch() {
String mark = "10";
String grade = switch (mark) {
case "10" -> "A";
case "9" -> "B";
case "8" -> "C";
default -> "F";
};
}
Jdk 15
text block ***
String 표현이 쉬워짐
@Test
void testTextBlock() {
String json = """
{
"name": "feature",\\
"pubdate": "2022-11-30
}
""";
System.out.println(json);
}
\ 를 넣으면 엔터를 무시
String formatter 함수 추가
기존 String.format 함수를 대체
@Test
void testStringFormatted() {
String oldStr = String.format("name: %s", "java");
String newStr = "name: %s".formatted("java");
}
NullPointException 메시지 정교화 ***
@Test
void testNPEMessage() {
LinkedList<String> names = null;
System.out.println(names.get(0).toUpperCase());
}
java.lang.NullPointerException: Cannot invoke "java.util.LinkedList.get(int)" because "names" is null
Jdk 16
toList() *
.collect(Collectors.toList()); 개선
@Test
void testStreamToList() {
List<Integer> nos1 = Stream.of(1, 2, 3).collect(Collectors.toList());
List<Integer> nos2 = Stream.of(1, 2, 3).toList();
}
MapMulti
@Test
void testStreamToList() {
List<Integer> result = Stream.of(1, 2, 3)
.mapMulti((Integer no, Consumer<Integer> consumer) -> {
consumer.accept(no);
consumer.accept(-no);
}).toList();
System.out.println(result);
}
output
[1, -1, 2, -2, 3, -3]
instanceof 패턴 변수 추가
표현이 단순해짐
@Test
void testInstanceOf() {
Object a = "hi";
if (a instanceof String) { // 패턴 변수
String s = (String) a;
System.out.println(s);
}
if(a instanceof String s) {// 패턴 변수
System.out.println(s);
}
}
기본 equals 함수에서도 변경
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
record 클래스 ***
kotlin의 data 클래스 와 비슷
record 클래스 특징
- 파라미터를 가진 생성자, 같은 이름의 메서드
- 기본 생성자가 없음
- final 클래스
- 변경 메서드(setter)가 없음, 다른 클래스 상속 불가
@Test
void testRecord() {
FullName fullName = new FullName("f", "l");
System.out.println(fullName.first());
System.out.println(fullName.last());
System.out.println(fullName.formatter());
}
record FullName(String first, String last) {
public String formatter() {
return first + " " + last;
}
}
Jdk 17
sealed 클래스 & 인터페이스
Sealed Class
Interface는 간단하게 상속하거나(extends), 구현(implements)할 클래스를 지정해두고(제한) 해당 클래스들만 상속 혹은 구현을 허용하는 키워드
인터페이스가 상속할 클래스를 지정해준다
public sealed interface FList<T> permits Cons, Empty {
}
public final class Cons<T> implements FList<T> {
private T head;
private FList<T> tail;
public Cons(T head, FList<T> tail) {
this.head = head;
this.tail = tail;
}
}
public final class Empty<T> implements FList<T> {
}
- 상속한 타입은 final, sealed(permit), non-sealed 를 포함해야함
- 상속한 타입은 같은 파일에 존재해야함
- record 타입도 상속 가능
풀 코드 (test 코드 형태)
package com.inspire12.practice.api.lab.java;
import static java.util.stream.Collectors.toList;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
class JdkNewFeatureTest {
// jdk 9
@Test
void testTryWithResource() throws IOException {
BufferedReader br = Files.newBufferedReader(Paths.get("a.txt"));
try (br) {
String line = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
void testCollection() {
List<String> list = List.of("hi");
Map<String, String> map = Map.of("a", "aa");
Set<Integer> set = Set.of(1, 2, 3);
}
@Test
void testArrays() {
List<String> a = List.of("hi");
List<String> b = List.of("hi");
int comp = Arrays.compare(a.toArray(new String[0]), b.toArray(new String[0]));
int firstIdx = Arrays.mismatch(a.toArray(new String[0]), b.toArray(new String[0]));
System.out.println(comp);
System.out.println(firstIdx);
}
// jdk 10
@Test
void testTypeReferences() {
var a = 10;
var result = new ArrayList<Integer>();
// jdk 11 부터는 람다식에 사용 가능
}
// jdk 11
@Test
void testString() {
String line = "line\\nline\\n";
// System.out.println(line.isBlank());
Stream<String> lines = line.lines();
// Stream<String> lines = Arrays.stream(line.split("\\n"));
System.out.println("lines test");
lines.forEach(System.out::println);
String line20 = line.repeat(10);
System.out.println(line20);
String lineStrip = " hello ";
System.out.println(lineStrip.strip());
System.out.println(lineStrip.stripLeading());
System.out.println(lineStrip.stripTrailing());
}
@Test
void testFiles() throws IOException {
Files.writeString(Path.of("a.txt"), "string", StandardOpenOption.CREATE);
String str = Files.readString(Path.of("a.txt"));
System.out.println(str);
}
// Jdk 12
@Test
void testStringJdk12() {
String a = "abc\\ndef".indent(3);
System.out.println(a);
}
// Jdk 12
@Test
void testDateUtil() {
var a = "20221121".transform(this::toLocalDate);
System.out.println(a);
}
private LocalDate toLocalDate(String dateInString) {
return LocalDate.parse(dateInString, DateTimeFormatter.BASIC_ISO_DATE);
}
// Jdk 14
@Test
void testSwitch() {
var randomNames = new String [] {"Jayden", "Bernard", "Zino", "Mason", "Elvin"}[(int) (Math.random() * 5)];
String name = switch(randomNames) {
case "Jayden", "jayden" -> {
System.out.println("Me!");
yield "제이든";
}
case "Bernard", "bernard" -> "버나드";
case "Zino" -> "자이노";
case "Mason" -> "메이슨";
case "Elvin" -> "엘빈";
default -> "What's your name";
};
System.out.println(name);
}
// Jdk 15
@Test
void testTextBlock() {
String json = """
{
"name": "feature",\\
"pubdate": "2022-11-30
}
""";
System.out.println(json);
}
@Test
void testStringFormatted() {
String oldStr = String.format("name: %s", "java");
String newStr = "name: %s".formatted("java");
}
//
@Test
void testNPEMessage() {
LinkedList<String> names = null;
System.out.println(names.get(0).toUpperCase());
}
// jdk 16
@Test
void testStreamToList() {
List<Integer> nos1 = Stream.of(1, 2, 3).collect(toList());
List<Integer> nos2 = Stream.of(1, 2, 3).toList();
List<Integer> result = Stream.of(1, 2, 3)
.mapMulti((Integer no, Consumer<Integer> consumer) -> {
consumer.accept(no);
consumer.accept(-no);
}).toList();
System.out.println(result);
}
@Test
void testMapMulti() {
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
double percentage = .01;
List<Double> evenDoubles = integers.stream()
.<Double>mapMulti((integer, consumer) -> {
if (integer % 2 == 0) {
consumer.accept((double) integer * ( 1 + percentage));
}
})
.toList();
System.out.println(evenDoubles);
}
@Test
void testInstanceOf() {
Object a = "hi";
if (a instanceof String) { // 패턴 변수
String s = (String) a;
System.out.println(s);
}
if(a instanceof String s) {// 패턴 변수
System.out.println(s);
}
}
/**
* 파라미터를 가진 생성자, 같은 이름의 메서드
* 기본 생성자가 없음
* final 클래스
* 값 변경 메서드가 없음, 다른 클래스 상속 불가
*/
@Test
void testRecord() {
FullName fullName = new FullName("f", "l");
System.out.println(fullName.first());
System.out.println(fullName.last());
System.out.println(fullName.formatter());
}
record FullName(String first, String last) {
public String formatter() {
return first + " " + last;
}
}
public sealed interface FList<T> permits Cons, Empty {
}
public final class Cons<T> implements FList<T> {
private T head;
private FList<T> tail;
public Cons(T head, FList<T> tail) {
this.head = head;
this.tail = tail;
}
}
public non-sealed class Empty<T> implements FList<T> {
}
}