[Java] Java8 에 새롭게 추가된 기능을 알아보자

2021. 4. 12. 09:59Java

Java 8 새롭게 추가된 기능을 알아보자.

- SAM interface

- Lambda

- Stream

- DateTime

- Optional

- Default Method

 

 

함수형 인터페이스

- Abstract Method를 오직 1개만 가진 interface

- SAM(Single Abstract Method) interface

- 인터페이스에 @FunctionalInterface을 사용한다. (자바가 제공하는 어노테이션이라 import 필요 없음)

- 즉 @FunctionalInterface 어노테이션이 붙어 있으면 함수형 인터페이스라고 생각하면 된다.

 

그러면 왜 SAM 으로 인터페이스를 만드는 걸까? 람다로 바꾸어 사용하기 위해서이다.

 

추상 메서드가 오직 1개만 존재하여야 람다로 사용할 수 있기 때문에 SAM 형태로 인터페이스를 만들었고, 이것을 함수형 인터페이스라 부르기로 하였다. 그러면 이 SAM interface를 사용하는 람다에 대해서도 알아보자.

 

Lambda

- 람다는 익명 함수이다. 메서드를 하나의 '식(expression)'으로 표현한 것

- 불필요한 코드를 줄이고 가독성을 향상하는 것이 목적이다.
- 메서드를 람다식으로 표현하면 메서드의 이름과 반환 값이 없어지므로, 람다식을 '익명 함수(anonymous function)'이라고도 한다.

 

Stream

배열, 리스트 등 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자

- 연속된 정보를 처리하기 위해 사용하는 클래스 (순차적으로 데이터를 처리)

- 스트림의 주요 연산자에는 map(), flatMap(), reduce(), collect(), filter(), forEach()가 있다.

 

- map()
스트림의 요소에 저장된 값 중에서 원하는 필드로만 뽑아내거나 특정 형태로 변환해야 할 때 사용

 

- flatMap()
스트림의 요소가 배열이거나 map()의 연산 결과가 배열인 경우, 즉 스트림의 타입이 Stream<T[]>인 경우, Stream로 다루는 것이 더 편리할 때가 있다. 

- reduce()
스트림의 요소를 줄여나가면서 연산을 수행하고 최종 결과를 반환

 

- collect()
스트림의 요소를 수집하는 최종 연산

 

- filter()
데이터를 조건으로 거를 때 주로 사용한다.

 

- forEach()
for 반복문처럼 각각의 항목에 접근할 때 사용한다.

 

Stream의 특징

1. Stream은 데이터를 변경하지 않습니다.

    - 스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐, 데이터 소스를 변경하지 않는다.

    - 필요하다면, 정렬된 결과를 컬렉션이나 배열에 담아서 반환할 수 있다.
2. Stream은 1회용입니다.

    - 스트림도 한번 사용하면 닫혀서 다시 사용할 수 없다. 필요하다면 스트림을 다시 생성해야 한다.
3. Stream은 지연 연산(Lazy Evaluation)을 수행합니다
    - 불필요한 연산을 피하기 위해 연산을 지연시키는 것
4. Stream은 병렬 실행이 가능합니다

    - 병렬 스트림은 내부적으로 fork & join 프레임웍을 이용해서 자동적으로 연산을 병렬로 수행한다.

    - 우리가 할 일이라고는 그저 스트림에 parallel()이라는 메서드를 호출해서 병렬로 연산을 수행하도록 지시하면 된다.

    - 병렬로 처리되지 않게 하려면 sequential()을 호출하면 된다.

    - 모든 스트림은 기본적으로 병렬 스트림이 아니므로 sequential()을 호출할 필요가 없다. 

 

DateTime

오랜 시간 동안 Java에서 제공하는 Date, Time API는 부족한 기능 지원을 포함한 여러 가지 문제점을 가지고 있었다. 예를 들어 java.util.Date와 SimpleDateFormatter는 Thread-Safe 하지 않아서 잠재적인 동시성 문제(다중 스레드 환경 문제)를 가지고 있다. 또한 몇몇 클래스들은 형편없이 디자인되어있는데 java.util.Date는 1900년도부터 시작한다거나 월(month)은 1부터 시작 하지만 일(day)는 0부터 시작하는 등 매우 직관적이지 않도록 설계되어 있다. 이런 이슈들을 포함한 다양한 원인들로 인해 몇몇 개발자들은 Joda-Time 같은 써드파티 라이브러리를 사용하기도 한다. 


JDK 코어에서 이런 문제점들을 해결하고 더 좋고 직관적인 API들을 제공하기 위해 새롭게 재 디자인한 Date, Time API를 Java SE 8부터 제공하기로 했다. 

Java SE 8은 java.time 패키지에 포함된 새로운 날짜와 시간 API를 장착했고 이를 통해 개발자에게 기존에 비해 훨씬 더 훌륭한 안정성과 기능을 제공하며, 새로운 API는 다양한 사용 사례를 포함하고 폭넓게 사용될 수 있도록 잘 구성되어 있다.

기존 Date, Calander와 달리 Thread Safe 하고, 날짜 연산 관련된 편의 기능이 많고, TimeOffset/TimeZone 관련된 기능들도 있어서 글로벌 서비스에서도 적합하다.

 

java.time.LocalDate - 날짜(시간 포함하지 않음), 타임존 사용하지 않음.

java.time.LocalTime - 시간(날짜 포함하지 않음), 타임존 사용하지 않음.

java.time.LocalDateTime - 날짜 및 시간, 타임존 사용하지 않음.

java.time.ZonedDateTime - 날짜 및 시간, 타임존 사용.

java.time.DateTimeFormatter - java.time에 대한 형식 (날짜 -> 텍스트), 변환 (텍스트 -> 날짜)

java.time.Duration - 시간을 초 단위 및 나노초 단위로 측정한다.

java.time.Period - 시간을 년, 월, 일로 측정한다.

java.time.TemporalAdjuster - 날짜를 조정한다.

 

Optional

- Optional은 null을 대신하기 위해 만들어진 새로운 코어 라이브러리 데이터 타입입니다.
- Optional 클래스는 null이나 null이 아닌 값을 담을 수 있는 클래스입니다. 

- 메서드가 반환할 결괏값이 ‘없음’을 명백하게 표현할 필요가 있고, null을 반환하면 에러를 유발할 가능성이 높은 상황에서 메서드의 반환 타입으로 Optional을 사용하자는 것이 Optional을 만든 주된 목적이다.

 

Default Method

- 인터페이스의 디폴트 메서드는 인터페이스의 강제성을 조금 유연하게 해주는 기능
- 만약 이 느슨한 것을 다시 엄격하게 수정할 필요가 있다면 인터페이스를 상속하여, 다시 추상 메서드로 재 선언하는 것도 방법이다.
- 자바 8에서 디폴트 메서드를 허용한 이유는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위함이다.
- 만약 구현 객체에서 인터페이스의 디폴트 메서드가 적절하지 못한다면, 오버라이딩 후 수정하여 사용하면 된다.
- 디폴트 메서드는 상속 또는 오버라이딩 또는 무시하면 된다.
- 다시 말해서 오버라이딩은 느슨한 규칙을 엄격하게 바꾸는 기능이다.