스터디 노트

자바 JDK 9 부터 JDK17까지의 주요 코딩 특징

데구루_ 2023. 10. 12. 20:57

아래 자료들은 최범균님의 영상을 보고 제작하였습니다.

원본 강의 영상 : https://youtu.be/7SlDdzVk6GE?si=adWuAndOQHY-40Pk 

JDK 9

📌 JDK8에서 인터페이스에 default 메서드와 static 메서드를 선언할 수 있게 되었는데, 9에서는 private 메서드도 추가할 수 있게 되었음.

public interface Client {
  void exchange();
  
  default void get() {
    logging2("before");
    exchange();
    logging("call get");
  }
  
  private void logging(String msg) {
    System.out.println(msg);
  }
  
  private void logging2(String msg) {
    System.out.println(msg);
  }
}

📌 try-with-resource 문법을 사용할 수 있음.

// JDK 9 이전
// try 구문에 변수를 넣어주면 에러가 나며 구문 안에서 선언해주어야 함.
BufferedReader br = Files.newBufferedReader(Paths.get("a.txt"));
try (br) { // Error ❌
  String line = br.readLine();
  ...
} catch (IOException e) {
  e.printStackTrace();
}

// JDK 9
// 괄호 안에 실질적인 final 변수면 넣어줄 수 있게 되었음.
BufferedReader br = Files.newBufferedReader(Paths.get("a.txt"));
try (br) { // Non error ⭕
  String line = br.readLine();
  ...
} catch (IOException e) {
  e.printStackTrace();
}

📌 Collection Factory Method

List<Integer> list = List.of(1, 2, 3);
Map<String, Integer> map = Map.of("a", 1, "b", 2);
Set<Integer> set = Set.of(1, 2, 3);

콜렉션 구현체에 of()라는 팩토리 메소드가 생겼음.

이전까지는 Arrays.asList(...)를 통해 만들었어야 했음.

📌 Arrays(compare, mismath)

int comp = Arrays.compare(a, b); // 두 배열을 비교하는 것
int firstIdx = Arrays.mismatch(a, b); // 두 배열이 어디에서 다른지 비교

 

JDK 10

📌 Var

var a = 10; // a는 int 타입
var result = new ArrayList<Integer>();

로컬 변수 타입 추론 기능이 추가됨.

11에서는 람다식에서도 사용이 가능해짐.

 

JDK 11

📌 String 클래스 유용한 메소드들 추가

- isBlank() : 공백문자만 포함했는지 여부

ex) Character.isWhitespace(int);

- lines() : 라인이 스트림으로 제공이 됨

ex) Stream<String> lines = name.lines();

- repeat() : 문자열의 반복

ex) String str = "1".repeat(10);

- strip(), stripLeading(), stripTrailing() : 공백문자에 해당하는 앞 뒤 문자를 제거함.

ex) Character.isWhitespace(int);

 

📌 Files

Files클래스에 문자열을 통으로 저장하고 읽어올 수 있는 메소드가 추가되었음.

- Files.writeString()

- Files.readString()

Files.writeString(Path.of("a.txt"), "string", StandardOpenOption.CREATE);
String str = Files.readString(Path.of("a.txt"));

기본 인코딩은 UTF-8

 

JDK 12

📌 String 클래스에 메소드 추가

- indent(int n) : n 만큼 들여쓰기 또는 내어쓰기

- <R> R transform(Function<? super String, ? extends R> f) : 문자열을 다른 타입으로 바꿀 때 사용

 

JDK 14

📌 Switch 식

String grade = switch (mark) {
  case 10 -> "A";
  case 9 -> "B";
  case 8 -> "C";
  case 7 -> "D";
  default -> "F";
};

switch문을 통해 값을 만들어낼 수 있으며 변수에 할당할 수 있고 return도 바로 가능함.

대신 case -> 형식으로 문법이 바뀌었으며 무조건 값을 만들어야함.

 

JDK 15

📌 Text block

String json = """
  {
    "name" : "json",
    "pubdate" : "2023-10-12"
  }""";

여러 줄에 걸쳐서 문자열을 입력이 가능해졌음.

 

📌 String.formatted()

String old = String.format("name: %s", "java"); // 15 이전
String v15 = "name: %s".formatted("java"); // 15 이후

 

📌 NPE 메시지 개선

System.out.println(name.getFirst().toUpperCase());

위에서 NPE 발생 시 다음과 같이 친절하게 오류 설명을 해줌.

java.lang.NullPointerException: Cannot invoke "String.toUpperCase()"
because the return value of "ver15.Name.getFirst()" is null

 

JDK 16

📌 Stream.toList() & Stream.mapMulti()

// toList()
List<Integer> nos1 = Stream.of(1, 2, 3).filter(n -> n % 2 == 0).toList(); // jdk 16
List<Integer> nos2 = Stream.of(1, 2, 3).filter(n -> n % 2 == 0).collect(Collectors.toList()); // 이전 버전

// mapMulti()
List<Integer> result = Stream.of(1, 2, 3)
    .mapMulti((Integer no, Consumer<Integer> consumer) -> {
      consumer.accept(no);
      consumer.accpet(-no);
    }).toList();

 

📌 instanceof와 패턴 매칭

if (a instanceof String s) {  // s : 패턴 변수
  System.out.println(s);
}

if (a instanceof String t && t.isBlank()) {
  System.out.println("blank");
}

if (!(a instanceof String b)) {
  return;
}
System.out.println(b.toUpperCase());

맨 위의 코드를 보면 만약 a가 String이면 s라는 변수가 만들어저 if 블록 내에서 s를 활용할 수 있게 된다.

또한 t를 바로 활용하여 t.isBlank()로 바로 활용할 수 있다.

세번째는 return을 바로 하면(early return) b를 그 다음에서 바로 활용할 수 있다.

 

📌 Record Class

record FullName(String first, String last) {
  public String formatter() {
    return first + " " + last;
  }
}

FullName fn = new FullName("f", "l");
fn.first();
fn.last();

Recod Class 내의 first, last 필드에 대해

- private final 필드로 선언

- 파라미터를 기본으로 가진 생성자가 선언됨

- 같은 이름의 메서드가 만들어짐(getter)

- hashCode, equals, toString이 기본으로 만들어짐

 

특징

- 기본 생성자 없음(무조건 값을 받아야 함)

- 값 변경 메서드 없음(필드가 final이기 때문에)

- final 클래스(추상클래스 아님)

- 다른 클래스 상속 불가

 

JDK 17

원본 강의 영상 : https://youtu.be/GJB-RyHKHjY?si=k0VF9Ias7QtRKgVZ

📌 sealed class & interface

public sealed interace 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 implements FList<T> {
  //
}

sealed는 상속할 수 있는 타입을 제한할 수 있는 기능을 제공한다.

class나 interface 앞에 선언해서 사용할 수 있다.

permits은 extends 뒤에 붙게 된다. 허용하는 하위 타입 목록을 지정할 수 있게되고 하위 타입이 존재해야만 한다.

즉, sealed와 permits는 한몸으로 사용한다.

 

그리고 sealed 타입을 상속한 타입은 제약이 있다.

- 클래스에 final를 붙여서 상속 불가 상태로 만든다.

- sealed(+permits)를 붙여서 다시 상속 가능한 상태로 만든다.

- non-sealed를 붙여서 제한하지 않음을 선언해야 한다.

 

또한  sealed 타입과 같은 패키지에 위치해야하거나 같은 모듈 내에 위치해야 한다.

 

단, 같은 java 파일 내에 하위 타입이 위치하면 permits를 생략할 수 있다.

// Cons Class
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;
  }
  ...
}

// 하위 클래스
final class SomeCons<T> extends Cons<T> {
  public SomeCons(T head, FList<T> tail) {
    super(head, tail);
  }
}

record 클래스 또한 sealed interface를 상속받아 사용할 수 있다.

public sealed interface FList<T> permits Cons, Empty {
  // ...
}

public record Cons<T>(T head, FList<T> tail) implements FList<T> {
  // ...
}

 

📌 switch에서 패턴 매칭

// jdk 17
return switch(flist) {
  case Cons c -> 1 + c.getTail().size();
  default -> 0;
}

// jdk 16
if (a instanceof String t && t.isBlank()) {
  System.out.println("black");
}