[리팩토링]Java 1.8 vs Java 17

현재 버전

  • Java 1.8
  • Spring Boot 2.4.5

자바 1.8의 특징

  • 펼치기

    람다표현식의 등장

    • 자바 8 에서 등장한 기능입니다.
    • 개요
      • 이름이 없는 함수
      • 쓰레드 , 컬렉션을 다룰 때 매우 유용함
    • 문법
      • 기본 문법은 ( 매개변수 ) -> { 실행 코드 } 로 구성되어 있습니다.
      • 예를들어
        • a + b 의 기능을 하는 함수의 경우
          (a, b) -> { return a + b; }
        • 람다 표현식 내부의 변수는 final 혹은 사실상의 final 이여야 합니다.
    • 함수형 인터페이스에서의 사용
      • 개요
        • 함수형 인터페이스
          • 단 하나의 추상 메서드를 가진 인터페이스를 의미합니다.
        @FunctionalInterface
        public interface MyFunctionalInterface {
            int add(int a, int b);
        }
        • 이러한 식으로 @FunctionalInterface 애노테이션을 통하여 함수형 인터페이스임을 명시적으로 선언이 가능합니다.
          MyFunctionalInterface lamb = (a , b) -> a + b;
          • 이런식으로 함수형 인터페이스의 경우 람다식으로 사용이 가능함.
          • 하지만, 단 하나 여야함.

    스트림 API ( Stream API )

    • 개요
      • 데이터를 효율적으로 처리할 수 있는 도구
      • 배열이나 컬렉션 등의 데이터를 빠르고 간편하게 처리가능
        • 라는데 사실 상황에 따라 많이 다르며, 일반적인 경우에는 스트림이 더 느린 경우가 있음
        • 일반적으로 가독성 향상이 된다는 점때문에 많이 사용한다.
        • 근래의 하드웨어 성능이 높아져 체감상의 차이는 거의 없다는점에 근거하여 속도는 크게 의미 없을 수 있다. (하지만 병렬처리의 경우에는 스트림이 우위임)
    • 작동 방식
      List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
      myList
          .stream()
          .filter(s -> s.startsWith("c"))
          .map(String::toUpperCase)
          .sorted()
          .forEach(System.out::println);
      • 리스트의 데이터를 필터링, 정렬, 출력 하는 일련의 작업을 메서드 체이닝 가능
    • 장점
      • 선언적인 코드
        • 어떻게 작동하는지 무엇을 하고싶은지 명시적으로 선언하여 코드에 대한 집중도가 상승
      • 병렬처리에 용이
        • 병렬처리를 쉽게 가능하여 병렬처리가 필요한 상황의 경우 성능 향상은 확실함.

    인터페이스 디폴트 메서드

    • 개요
      • 디폴트 메서드는 인터페이스에 새로운 메서드를 추가하는 경우
      • 기존에 그 인터페이스를 구현한 클래스를 깨뜨리지 않는 방법 제공
    • 동작 방식
      public interface MyInterface {
          default void myDefaultMethod() {
              System.out.println("Default implementation");
          }
      
          void myAbstractMethod();
      }
    • 장점
      • 유연성
        • 기존 코드를 변경하지 않아도, 인터페이스에 새로운 기능을 추가할 수 있음.
      • 하위 호환성
        • 새로운 기능을 추가해서 기존 작성 코드에 영향을 미치지 않음.

자바 8 자바 17 주요 변경점 개요

  1. LTS
    • Java 17 은 Long Term Support(LTS) 를 제공하는 버전이다.
    • Java 8 은 Oracle Premier Support 를 잃었기 때문에, 점차적으로 지원이 줄어들 것이다.
  1. 새로운 기능 개선 사항
    • Java17 에서는 패턴 매칭, 레코드 등의 기능이 추가되었으며
    • 코드를 더 깔끔하게 작성할 수 있는 차이점이 있음
  1. 성능
    • Java17 에서는 가비지 컬렉션, JIT 컴파일러 등의 여러 내부 매커니즘이 개선되었음.
  1. 라이브러리와 API
    • 새로운 라이브러리와 API 추가로 개발 과정의 효율화
  1. Oracle 지원
    • Java8에 대한 지원은 나날이 약해질 것으로 예상..
    • 반면 Java17 의 경우 오랫동안 안정적인 지원을 받을 수 있음.

Java 8 17 모두 별도의 장 단점이 있음. 하지만, Oracle 지원감소와 새로운 기능의 부재 등을 고려하였을때 Java 17 로의 전환은 필수적으로 보입니다.

전자정부 프레임워크도 결국 4.1에 접어들면서 Java 8 에 대한 공식적인 지원을 그만두고, Java 11 을 필수로 받아들였습니다.

이에 대비하기 위하여 결국 Java 11 혹은 그 이상으로 마이그레이션이 필요하다가 생각.

변경점 본론

일단 ! Java 8 vs Java17 이기에, 언제 등장했는지는 명확하게 표기하기 귀찮아서 안했습니다(일부는 함).

혹시 어느 자바 버전에서 등장한건가 하신분들은 추가적으로 찾아보세요


문법상의 변화

var 키워드의 등장

  • 자바 10에서 등장했습니다.
  1. 사용법
    • 간결한 코드 작성
      • var 키워드 사용시 변수의 타입을 명시적으로 적지 않아도 됨.
      • 코드의 간결성을 유지가능
    // Java 8 스타일
    Map<String, List<MyDtoType>> myMap = new HashMap<String, List<MyDtoType>>();
    
    // Java 10 이상에서의 "var" 키워드 사용
    var myMap = new HashMap<String, List<MyDtoType>>();
    • 가독성 고려
      • 다만 var 는 타입의 명시를 하지 않기에, 변수 이름이 해당 변수의 역할을 명확하게 표현해야합니다.
  1. 제한사항
    • 람다 표현식에서 사용 불가능
      • var 키워드를 람다 표현식의 변수에 할당할 수 없음.
      // 컴파일 에러
      var fun = MyObject::mySpecialFunction;
    • 람다의 인수에서 사용가능
      • 람다 표현식의 인수에 var 가능.
      • 인수에 어노테이션을 추가할 수 있다는 특징
      boolean isThereAneedle = stringsList.stream()
        .anyMatch((@NonNull var s) -> s.equals("needle"));

Record 의 등장

  • 정의
    • 데이터를 효율적으로 저장하고 관리할 수 있는 타입
  • 주요 특징
    1. 자동 생성되는 멤버
      • 자동으로 몇 가지 기본 멤버가 생성됨.
        1. private final 필드
        1. getter 메서드
        1. 생성자
        1. equalshashCode
        1. toString
    1. Lombok과 유사성
      1. Lombok 의 @Value 어노테이션과 유사
      1. Records 는 불변성을 가진 데이터를 표현하는데 사용됨.
      1. 필드를 선언시, 자동으로 코드가 생성됨.
    1. Enum 과 차이점
      1. Enum 과 달리 다른 클래스를 상속하거나 상속될 수 없음
      1. 인터페이스를 구현하거나 정적 필드와 메서드를 가질 수 있음
      1. new 키워드를 사용하여 인스턴스 생성이 가능
  • 사용법
    record BankAccount (String bankName, String accountNumber) implements HasAccountNumber {}
    • 요 한줄로 레코드를 간단히 생성가능
    • 생성자에서 유효성 검사가능
      record BankAccount (String bankName, String accountNumber) implements HasAccountNumber {
        public BankAccount {
          if (accountNumber == null || accountNumber.length() != 26) {
            throw new ValidationException("Account number invalid");
          }
        }
      }
      • 필드에 대한 명시적인 할당이 필요 없음.

Switch 문의 확장

  1. 개요
    • 기존 Java8 의 switch 문에서 조금 더 확장된 기능을 제공합니다.
  1. 기존적인 확장된 switch 표현식
    1. 그룹화와 값의 반환
      • 기존의 switch 문에서는 case 를 그룹화하거나 결과값을 반환할 때 상당이 복잡함
      • 하지만 확장된 switch 표현식에서는 이런 것이 간단해짐
      DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
      boolean freeDay = switch (dayOfWeek) {
          case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
          case SATURDAY, SUNDAY -> true;
      };
      • 월 ~ 금 까지는 false 반환
      • 토 ~ 일 까지는 true 반환
      • break 키워드가 없어도 됨.
      • switch 표현식 자체가 값을 반환하기에, 변수에 저장이 가능 😍
  1. yield 키워드와 복잡한 로직의 처리
    DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
    boolean freeDay = switch (dayOfWeek) {
        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
          System.out.println("Work work work");
          yield false;
        }
        case SATURDAY, SUNDAY -> {
          System.out.println("Yey, a free day!");
          yield true;
        }
    };
    1. yield 키워드 사용법
      1. case 블록 안에서 코드를 실행 한 후, 결과 값을 switch 표현식의 결과로 설정가능
    1. 복잡한 로직의 처리
      • 사실 기존 switch 사용방식을 람다식 처럼 사용하기 위한 하나의 포장방식 처럼 보인다.
      • return 키워드를 사용할 수 없으니 별도의 yield 키워드를 사용함으로서 멀티라인 로직을 수행할 수 있도록 도와주는 게 아닌가 생각된다.
      • 어려운게 아니라 그냥 return 의 역할과 비슷하다.
      • 단순히 case 내부에서 로직에 대한 반환값을 처리할 수 있게 된 것이다.

instanceof 패턴 매칭의 확장

  • Java 8 에서는 instanceof 사용시 타입 캐스팅을 수행 후, 추가 로직을 적용해야했다.
  • 하지만 17에서는 패턴 매칭을 활용한 새로운(?) 문법이 도입
  1. instanceof 패턴 매칭의 특징
    1. 기존의 문제점
      if (obj instanceof MyObject) {
        MyObject myObject = (MyObject) obj;
        // ... 추가 로직
      }
      • 별도로 타입을 체크한 후 명시적으로 캐스팅하여 변수를 별도로 로직안에서 선언을 해줘야했습니다.
    1. 새로ㅇㅇ
      if (obj instanceof MyObject myObject) {
        // ... 추가 로직
      }
      • 타입을 확인하는 동시에 지역변수를 선언할 수 있게 되었습니다.
    1. 조건문에서의 활용
      if (obj instanceof MyObject myObject && myObject.isValid()) {
        // ... 추가 로직
      }
      • 선언된 변수는 if 문 안에서 사용될 수 있습니다. ㄷㄷ..

    하지만.. instanceof 문법 자체는 자주 사용되는 문법이 아니기에.. 사실상 사용할 일이 잘 없을 것으로 예상됩니다.

Sealed Class

  1. 정의
    • 클래스 계층을 엄격히 제한하여 어떤 클래스가 상속을 허용할지 명시적으로 지정가능
    • switch || instanceof 에서 경고 메시지 관리도 가능
  1. 특징과 장단점
    1. 명확한 클래스 계층
      • 특정 클래스가 어떤 자식 클래스들만 허용하는지 명시적으로 표현가능
      • 프로그램의 안정성의 증대 가능
      public abstract sealed class Animal permits Dog, Cat {
      }
      public final class Dog extends Animal {
      }
      public final class Cat extends Animal {
      }
    1. 경고 메시지 제거
      1. 컴파일러의 경고 메시지
        • 컴파일러는 코드의 가능한 문제점을 미리 알려주기 위해 경고를 출력함
        • swich 혹은 instanceof 연산자 사용시 모든 경우를 다루지 않았다고 컴파일러가 경고 메시지를 줄 수 있음.
      1. sealed classed 의 역할
        • 특정 클래스가 어떤 서브 클래스만을 허용할지 명시함으로서
        if (animal instanceof Dog d) {
            return d.woof();
        } 
        else if (animal instanceof Cat c) {
            return c.meow();
        }
        • 에서 별도로 Gecko 를 명시적으로 허용했는데, 이를 고려하지 않았다! 라고 경고(에러아님) 를 출력해줄 수 있는 것이다.
    1. 하지만…
      • 괜한 코드의 복잡성만 높일 수 있으며
      • 순환 참조 같은 안티 패턴을 생성할 가능성이 있다고

      합니다.

      • 슈퍼 클래스에서 서브 클래스에 대해 명시적으로 알고 있으며, 그 반대도 알고 있는 경우,
      • 순환참조가 될 수 있다고 함.

TextBlocks 기능

  1. 정의
    • Java 13 부터 등장하였음
    • 긴 문자열을 훨씬 편리하게 다룰 수 있음.
    • 기존의 긴 문자열은 별도 이스케이핑문자 (\) 를 사용해야했지만, TextBlock 으로 불편이 줄었음
  1. 사용법
    String myWallOfText = """
    안녕하세요
    ㅋㅋ
    """;

    세 개의 따옴표 (”””) 를 사용하여 문자열을 선언가능

    String myWallOfText = """
    안녕하세요 \
    ㅋㅋ
    """;
    • \ 문자를 사용하면 문자열에 대해 별도 띄어쓰기를 적용하지 않는다.
    안녕하세요 ㅋㅋ
    • 로 출력된다.
    • 백슬래시를 사용하고 싶다면?
    String myWallOfText = """
    안녕하세요 \\
    ㅋㅋ
    """;
    • 별도 백슬래시 안에 이스케이핑처리를 해주긴해야함.
  1. 장점
    • JSON 혹은 XML 템플릿을 더 깔끔하게 유지할 수 있음.
    • 원래 자바에서는 xml 이나 json 을 String 변수에 담는 경우 상당히 지저분했음.

Better NPE (JVM 변경점)

  1. 정의
    • 기존의 Java에서는 NPE 가 발생하는 경우
    • company.getOwner().getAddress().getCity() 같은 메서드 체인에서 어느 한 부분이 null 인 경우
    • 어느 부분에서 null 이 발생했는지 정확하게 알 수 없었음.
    • 하지만 이제는 더 자세한 메시지를 통해 어떤 부분에서 문제가 발생했는지 확인이 가능하다.
  1. 기존의 문제점
    • NPE 발생시 어느 부분에서 발생했는지 디버깅으로 밖에 알 수 없었음
  1. 개선점
    • JVM 런타임에 바이트 코드 분석을 통해 더 자세한 메시지 제공
    • "cannot invoke Person.getAddress()"
      • 같은 메시지로 어떤 메서드 호출에서 문제가 발생한 건지 알 수 있게 됨.
    • JAVA 언어적 변경점 X → JVM 의 변경점

사실 자바 17 이라서 특이한것이 아니라 이전 버전에서 점진적으로 추가된 내용이 대부분입니다

자바 17에서는 SEALED CLASS , RECORD , INSTANCEOF 의 확장만 추가되었으며,

추가적으로는 JVM 관련 기능의 개선이 주요점입니다!

JVM 관련 기능의 개선

  1. GC
    • 더 효율적인 GC 알고리즘의 도입
      • 메모리 관리 효율성 증대
      • 응답 시간 개선
  1. AOT 컴파일러의 추가
    • Ahead-Of-Time 컴파일
      • 프로그램이 실행되기 전에 모든 코드를 네이티브 코드를 변환하는 방식
      • JIT 컴파일의 경우 프로그램이 실행되는 동안 필요한 코드만 네이티브로 변환하였음
      • 하지만 AOT 컴파일러가 추가됨으로서,
        • 애플리케이션의 빠른 시간 시간
        • JIT 컴파일러가 코드를 컴파일하는 데 드는 CPU 자원을 줄일 수 있게 됩니다.
        • 이는 MSA 의 경우 큰 장점으로 느껴질 수 밖에 없습니다.


Uploaded by N2T