-
Java 8 주요 업그레이드 사항Java, Kotlin, Spring 2022. 5. 25. 09:27
Java 8
람다식 (Lambda expressions)
람다식은 자바에서 함수를 일급 객체로 활용하기 위해 새로 추가된 표현식이다.
기존에는 함수를 다른 함수의 매개변수로 전달하기 위해서는 익명 클래스가 필요했다.
Comparator<Apple> byWeight = new Comparator<Apple>() { public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo(a2.getWeight()); } }
람다식은 불필요한 익명 클래스를 없에고 간단한 함수 표현식으로 함수의 일급 객체화를 가능하게 해준다.
Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
자세한 내용은 아래 2개의 포스트를 참고하자.
함수형 인터페이스
함수형 인터페이스는 메소드를 하나만 가지는 인터페이스다. 하나의 메소드를 가지는 인터페이스를 왜 특별히 선언하여 사용할까?
그 이유는 메소드 참조와 람다식의 사용을 위해서 필요하기 때문이다.
메소드 참조 혹은 람다식을 받는 메소드는 메소드 참조와 람다식을 표현해줄 자료형이 필요하다.
public void foo(BarInterface bar) { bar.say(); } @FunctionalInterface interface BarInterface { String say(); } class Bar implements BarInterface { String say() { return "Hello world!"; } } // 람다식으로 실행 foo(() -> "Hello world!"); // 메소드 참조로 실행 foo(Bar::say);
컴파일 언어인 자바는 컴파일 시점에 메소드 참조와 람다식의 실행이 안전하게 컴파일 될 수 있는지를 보장해야 한다.
만약 여러 메소드를 가지는 인터페이스가 자료형으로 허용된다면, 메소드 참조와 람다식을 컴파일 타임에 검증할 때 해당 인터페이스에 메소드 참조와 람다식을 허용하는 메소드가 선언되어 있는지 검사해야한다. 이것은 O(N) 의 시간이 걸리기 때문에 컴파일 시점에는 다소 부담이 있을 수 있어서 함수형 인터페이스가 메소드를 하나만 가지도록 제한하지 않았을까 하는 추측이 든다.
만약 2개 이상의 메소드가 함수형 인터페이스에 구현되어 있다면, 컴파일 타임에 에러를 발생시킨다.
디폴트 메소드
인터페이스에 기본 구현을 추가하는 기능이다. 이것은 엄청난 변화이다.
자바의 다중 상속 불가 문제를 해결할 수 있는 효율적인 대안이 되었고, 자바 코드의 방대함을 획기적으로 줄여주었다.
객체지향의 다형성은 본래 하나의 인터페이스를 공유하는 여러 인스턴스가 각각의 다른 구현을 통해 여러 역할을 할 수 있다는 점이 장점으로 부각되었다.
그러나 여러 역할을 할 필요가 없이 공통된 역할을 수행해야 하는 메소드가 있다면 어떨까? 인터페이스를 구현하는 모든 구현 클래스에서 반복적으로 구현해 주어야 한다. 이는 참 성가신 일이 아닐 수 없고, 코드의 중복을 야기한다. 물론 Composition 개념을 활용해 공통된 기능을 제공하는 별도의 클래스를 만들수도 있겠지만, 그 일을 행하기 위해서 또다시 역할과 책임에 대한 많은 고민이 불가피하다.
디폴트 메소드는 인터페이스에서 구현을 제공하는 것을 가능하게 해줌으로서 이 문제를 해결한다.
JVM의 변화
자바 8 이전에 Class 혹은 Method code 가 저장되는 영역은 JVM 내 메모리에서 관리되었다. 이름은 Permanent Generation (PermGen) 이다.
자바 8 부터는 JVM 내 메모리가 아닌 OS 가 관리하는 Native Memory 영역인 Metaspace 에서 담당한다.
Metaspace 특징
1. Heap 영역이 아니라 Native 메모리 영역이다.
2. Default 로 제한된 크기를 가지고 있지 않고, 동적으로 늘어난다.
3. Java 8 부터는 PermGen 관련 JVM 옵션은 무시한다.
더 자세한 내용은 https://goodgid.github.io/Java-8-JVM-Metaspace/ 참고하자.
병렬 배열 정렬
자바 8 버전의 Arrays(https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html) 에는 배열에 대해 병렬 처리를 하는 메소드들이 추가되었다. parallelSort, parallelSetAll 등이 있고, 멀티스레드 방식으로 병렬처리 한다.
복잡한 동기화와 스레드 관리에 대한 부분이 내부 구현으로 감춰져 있기 때문에 사용하는 개발자 입장에서 안전하게 믿고 사용할 수 있다.컬렉션을 위한 대용량 데이터 처리 (Stream API)
자바의 반복 가능한 자료구조에는 Iterable 인터페이스 부터 시작하는 Hierarchy 가 있다.
실제 구현하여 가장 많이 사용하는 인터페이스는 List, Queue, Set 이다.
3가지 인터페이스는 각각 제공하는 기능과 특징이 다르기 때문에 상황에 맞게 선택하여 사용하면 적재적소에 효율적으로 사용할 수 있다.
다만 여러개의 인터페이스로 나뉘어 있는 이 구조는 코드의 통합성을 헤친다.
특징은 모두 다르지만 Collection 인터페이스를 공통으로 상속하는만큼 반복가능한 자료구조인 점은 공통 사항임에도 불구하고, 구현 상 코드를 같은 방식으로 사용할 수가 없는 단점이 있다.
자바 8 에서는 Collection 인터페이스에 stream() 이라는 디폴트 메소드를 제공하여 일관된 Stream 구현체를 반환하도록 해준다.
따라서 List, Queue, Set 등 다양한 자료구조에서 Stream 이 제공하는 공통 인터페이스를 모두 사용하여 편리하게 개발할 수 있다.
Stream API 는 생성 -> 가공 -> 소비 의 단계를 거치며, 1개의 원소가 수직적으로 모든 API 를 순회하는 형태로 진행된다.
또한 함수형 프로그래밍 방식을 도입하여 병렬성을 쉽게 지원하도록 설계되었다.
(함수형 프로그래밍은 병렬성을 쉽게 한다. 그 이유는? 불변성)
Optional
https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
자바는 오랜 시간동안 null 에 대한 고민을 해왔다.
nullable 은 유연성을 더하지만 복잡성도 더하고, 예측가능하지 않은 런타임 에러를 일으킬 가능성이 있다.
Optional 은 모든 자바 객체가 nullable 을 기본으로 삼고있는 제약을 깨트리지 않으면서 null 에 대한 고민을 조금은 덜어낼 수 있는 현실적인 대안이 된다.
Optional 은 null 을 검사할 수 있는 기능을 더해놓은 Wrapper class 이다. null check 를 기본으로 하도록 강제함으로써 Null Point Exception 을 피할 수 있도록 해준다.
이런 점에서 Optional 을 사용하는 것과 사용하지 않는 것에는 많은 차이가 있다.
함수 하나를 작성한다고 생각해보자. Optional 을 리턴한다면 함수를 사용하는 개발자는 Optional.isPresent() 등의 API 로 해당 값이 유효한지 검사를 해야한다. 검사를 하지 않고 그냥 사용한다면 Optional.get() 하여 사용한다면 Warning 을 제공해줄 수가 있다.
사용하는 입장에서는 Optional 객체를 통해 검사를 하는 것과 객체의 인스턴스에 대해 직접 null 체크를 하는 것이 크게 다르지 않다고 생각할 수도 있지만, 위에서도 언급했듯이 강제성을 부여한 것과 그렇지 않은 것에는 언어적 관점에서 엄청난 차이가 있기에 Optional 은 의미있는 개선점이라고 생각한다.
Base64 인코딩과 디코딩을 위한 표준 API
https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
Base64 인코딩과 디코딩을 위한 표준 구현체를 제공한다. 이 클래스의 구현은 RFC 4648 및 RFC 2045에 지정된 스펙을 따라 구현되었다.
새로운 날짜, 시간 API
https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html
자바 8 이전에는 java.time 패키지에 많은 문제가 있었다.
기존 문제점
- jdk 1.1 부터 제공된 Calendar 클래스는 불변 객체가 아니라서 값이 수정될 수 있다.
- 윤초(leap second) 같은 특이 케이스가 고려되지 않았다.
- 월을 나타낼 때 1 ~ 12 가 아닌 0 ~ 11 로 표현해야 하는 불편함이 있었다.
위와 같은 문제점으로 인해 java.time 패키지를 그대로 사용하기 보다는 Joda-Time 라이브러리를 사용하여 불편함을 회피해왔다.
자바 8 부터는 LocalDate, LocalDateTime 과 같은 클래스를 제공함으로써 문제점들이 개선되었다.
반응형'Java, Kotlin, Spring' 카테고리의 다른 글
Spring data mongodb multiple database config 설정하기 (0) 2022.06.24 Spring Bean 스코프 (0) 2022.06.19 JVM 메모리 구조 (0) 2022.05.20 @SpringBootApplication 어노테이션 (0) 2022.05.20 Java Compile 과정 (0) 2022.05.07