Java 8 소개
들어가기전에..
2014년, Java의 새로운 버전(1.8)이 공개되었다. Java의 버전 업은 Evolution (기능적인 부분의 개선)과 Revoultion(언어 자체 형태의 변화)으로 나뉘는데, 이번 Java 8 버전 업은 Revolution에 속한다고 한다. Java 8에서 어떠한 기능들이 추가되었는지, 잘 정리된 여러 블로그의 글을 인용해 정리해보려고 한다.
인터페이스에 찾아온 변화
Java 8의 인터페이스는 default method
와 static method
를 포함하여 자체 구현을 가지게 되었다. default method
역시 이번 Java 8부터 추가 된 것으로, default 지시자를 이용해 기본 메소드의 정의가 가능하게되어 인터페이스를 구현하는 기존 코드의 변경없이 새 메소드의 추가가 가능해졌다. 원래 객체지향 언어에서 인터페이스는 그 시그너처와 선언이 변하지 않는다는 것을 전제로 하여 다형성(polymophism)을 정의하는 객체간의 규약이었지만, Java 8 인터페이스는 이를 확장하는 개념을 도입하였다. 이로 인해 기존 인터페이스에 비해 애매모호하다는 점과 C++과 같이 다중 상속이라는 두 가지 문제점이 예상된다. 아래는 다중 상속으로 인한 하위 클래스의 문제점을 표현한 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface A {
default void foo() { … }
}
public interface B {
default void foo() { … }
}
public class Z implements A, B {
@Override
public void foo() {
A.super.foo(); // or B.super.foo();
}
}
람다 표현식 지원
Java 8에 추가된 기능 중 가장 큰 변화라고 생각한다. Java 8 에서는 ‘단 하나의 메서드만 선언된 Interface’만을 람다로 표현할 수 있도록 강제하여 함수형 프로그래밍을 가능하게 한다. 여기서 정의되는 인터페이스를 ‘함수형 인터페이스’라고 정의한다. 람다식을 이용하면 동작과 데이터를 모두 동적으로 설정하는것이 가능해진다. 아래의 예제들은 모두 왼쪽이 입력값이 되고, 오른쪽이 동작에 대한 정의이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Concatenating strings
(String s1, String s2) -> s1+s2;
// Squaring up two integers
(i1, i2) -> i1*i2;
// Summing up the trades quantity
(Trade t1, Trade t2) -> {
t1.setQuantity(t1.getQuantity() + t2.getQuantity());
return t1;
};
// Expecting no arguments and invoking another method
() -> doSomething();
람다 표현은 화살표를 중심으로 두 부분으로 나뉘어진다. 왼쪽은 메소드의 인수이고 오른쪽은 이 인수로 할 일인데 예를 들어 비즈니스 로직 같은 것이다. 본문은 하나의 표현식이거나 코드 블록이고 결과값을 반환할 수도 있다.
첫 번째 람다 표현 (String s1, String s2) → s1+s2에서 화살표(→)의 왼편이 메소드의 인수 리스트로 두 개의 문자열로 이루어져 있다. 메소드의 오른편을 보면 이 메소드로 구현하려는 로직을 볼 수 있다.
위의 예는 두 개의 문자열이 주어졌을 때 그 둘을 합치는 것이다. 메소드에 로직을 넣으려면 화살표의 오른쪽에 오면 되는데 앞의 예에서 로직은 두 개의 인수를 더하는 것이다. 오른편에 올 수 있는 것은 문장, 표현식, 코드 블록, 다른 메소드 호출 등이다.
java.util.stream
Java 8은 단순히 람다 표현식을 도입할 뿐 아니라 효과적인 사용 방법을 안내하고 촉진할 수 있도록 기존 API에 람다 표현식을 대폭 적용했다. 그 대표적인 인터페이스가 Stream이다. Stream 인터페이스는 컬랙션(Collection)을 다루는 새로운 방법을 제공한다. 스트림은 컬랙션을 파이프식으로 처리하도록 하면서 고차함수로 그 구조를 추상화한다. 그래서 지연 연산이나 병렬 처리 등이 동일 인터페이스로 제공된다. 좀더 알기쉽게 설명하자면 배열이나 리스트, 맵으로 대표되는 컬랙션을 스트림으로 다룰 수 있게 되었다는 것 이다. 다음은 컬랙션에 대한 스트림화의 예이다.
1
Stream <T> stream = collection.stream ();
이것이 함수형 프로그래밍과 결합하면 다음과 같은 형태가 된다.
1
2
3
int sumOfWeights = blocks.stream () filter (b -> b.getColor () == RED)
. mapToInt (b -> b.getWeight ())
. sum ();
위의 샘플코드는 stream패키지의 Javadoc에 실린 예로서, stream의 소스로서 blocks라는 Collection을 사용하고 있다. 그 스트림에 대해 filter-map-reduce를 실행하여 붉은색(RED)블록에 대한 무게(weight)의 합(sum)을 구하는 일련의 과정이 한줄의 코드에 집약되어 표현되고 있다.
java.time
새로운 날짜/시간 관련 API가 java.time 패키지에 추가되었다. 과거에 Java에서 날짜/시간 관련해서 Java에서 제공하는 API보다 Joda Time이라는 외부 라이브러리가 더 편리하다는 이야기를 들은 적이 있는데, 아마 이를 개선한 것 같다. 새로운 날짜/시간 클래스는 immutable이며 스레드에 대해 안전하다. 날짜 및 시간 형식으로 Instant, LocalDate, LocalDateTime, ZonedDateTime이 추가되었으며 날짜와 시간 이외의 것으로서 Duration과 Period가 추가되었다. 새로 추가된 값 형식은 Month, DayOfWeek, Year, Month YearMonth, MonthDay, OffsetTime, OffsetDateTime등이 있다. 이런한 새로운 날짜/시간 클래스는 대부분이 JDBC에서 지원됨으로서 RDB연동의 효율적인 구현이 가능하다.
참고 및 인용한 블로그
- 한빛미디어 : 자바 8에서 새로워진 점 : 람다
- More Agile : Java 8 살펴보기
- Xenomity Blog
- 생각하고 나누고 공감하기… : Java 8 개선 사항 관련 글 모음
- Clean Code that Works. : 자바 스트림 API
- Naver D2 : Java의 날짜와 시간 API
Java 8 의 모든 기능들은 여기에 정의되어 있다.