영감을 (inspire) 주고픈 개발 블로그

java 9 ~ 16 + 17 특징 정리 본문

개발/디테일

java 9 ~ 16 + 17 특징 정리

inspire12 2022. 11. 28. 19:44

이번에 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 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> {

    }
}

 

 

 

반응형