오늘은 미리보는 java9의 새로운 기능들을 살펴보자. 물론 지금은 릴리즈전이라 바뀔 내용이 있을 수 있으니 너무 깊게는 살펴보지 말자.

조만간 java9가 릴리즈 될 예정이다. 원래 일정은 올해 초에 릴리즈 될거라고 했었는데 일정이 밀렸다. 왜 밀린지는 모르겠지만.. 아무튼 담달 27일인 7월27일에 다시 릴리즈 예정일이다. 역시 또 밀릴지는 의문이다.
(수정) 또 다시 딜레이 되었다고 한다. 릴리즈 일정은 아래와 같다.

2017/07/06      Final Release Candidate
2017/09/21      General Availability

그전에 안타까운 소식이 하나 있다. java7부터 언급이 많이 되었던 직소(Jigsaw) 프로젝트가 JCP에 통과하지 못하는 일이 발생하였다. 대부분의 회사들이 반대표를 던졌다. oracle, intel , Azul Systems등이 찬성표를 던졌으나, 이클립스 재단, IBM, 트위터, 레드햇(개빈 킹이겠지?) 등 이 반대표를 던저 23개 회사중 13개의 회사가 반대를 하였다.
그래서 한달이내로 다시 리뷰를 받아야 되는데 해결할 일이 많다고 한다. 그 한달이 거의 다 된거 같은데.. 조만간 소식이 들릴듯 하다. 그래서 여기서는 직소(Jigsaw)는 언급하지 않겠다. 양도 꽤 있어서 만약 java9에 들어간다면 그때 다시..

Collections 의 팩토리 메서드

List에는 Arrays 클래스에 존재 했던거지만 List 인터페이스에 새로운 팩토리메서드가 추가로 생겼다.

List.of(1,2,3,4,5);

위와 같이 간단하게 팩토리 메서드를 호출해서 생성할 수 있다. 메서드 형태들는 다음과 같다.

static <E> List<E> of(E e1) 
static <E> List<E> of(E e1, E e2)
static <E> List<E> of(E e1, E e2, E e3)
...
static <E> List<E> of(E... elements)

List에만 생겼다면 그닥 의미가 별로 없었을 것이다. 하지만 Set, Map에도 추가 되었다.

Set.of(1,2,3,4,5);
Map.of("key1", "value1", "key2","value2");

Set의 경우에는 중복을 허용하지 않는다. 그래서 만약 같은 값을 넣을 경우에는 에러를 발생시킨다.

Set.of(1,2,3,4,1);

... java.lang.IllegalArgumentException: duplicate element: 1
...

또 한 Map도 키는 중복을 허용하지 않는다. 그래서 Set과 마찬가지로 같은 키값을 넣을 경우 에러를 발생시킨다.

Map.of("key1", "value1", "key1","value2");

참고로 위에 모든 컬렉션은 immutable하다. 변경할 수 없다. 만약 List.of()로 생성하고 add() 메서드를 호출하면 UnsupportedOperationException 이 발생한다.

좀 더 쉽게 Set과 Map을 사용할 수 있어서 괜찮은거 같다.

Stream API

Stream에 몇가지 API가 추가 되었다. takeWhile(), dropWhile(), ofNullable()가 추가 되었다.

takeWhile

takeWhile() 의 메서드 형태는 다음과 같다.

default Stream<T> takeWhile(Predicate<? super T> predicate)

이 아이는 특정한 엘리먼트까지 왔다면 멈추고 그 엘리먼트까지 반환한다. Predicate을 파라미터로 받으니 boolean 값을 리턴하면 된다.

List<Integer> numbers = List.of(1, 3, 7, 8, 15, 4)
        .stream()
        .takeWhile(i -> i < 10)
        .collect(toList());

filter와 유사하다. 하지만 filter와 다른점은 해당 조건까지 왔다면 멈추고 반환한다는 것이다. filter와 비교해보자.

List<Integer> numbers = List.of(1, 3, 7, 8, 15, 4)
        .stream()
        .filter(i -> i < 10)
        .collect(toList());

filter를 사용했을 경우에는 1, 3, 7, 8, 4를 반환하는 반면에 takeWhile를 사용할 경우에는 1, 3, 7, 8까지 데이터를 반환한다.

dropWhile

dropWhile 의 메서드 형태는 다음 과 같다.

default Stream<T> dropWhile(Predicate<? super T> predicate)

dropWhile() 메서드는 takeWhile() 메서드와 반대 개념이다. takeWhile()메서드가 1, 3, 7, 8를 반환하였다면 dropWhile() 메서드는 나머지인 15, 4를 반환한다.

List<Integer> numbers = List.of(1, 3, 7, 8, 15, 4)
        .stream()
        .dropWhile(i -> i < 10)
        .collect(toList());
//[15, 4]

ofNullable

ofNullable의 메서드 형태는 다음 과 같다.

public static<T> Stream<T> ofNullable(T t) 

ofNullable() 메서드는 Optional.ofNullable() 과 동일하다. null safe한 메서드 이다.

Stream.ofNullable(null)

위와 같이 작성해도 에러는 발생하지 않는다.

Optional API

Optional에도 몇가지 API가 추가 되었다. stream(), or(), ifPresentOrElse() 메서드가 추가되었다.

stream

메서드 명 그대로 Optional을 Stream 타입으로 변경하는 메서드 이다. 메서드 형태는 다음과 같다.

public Stream<T> stream()

Optional 타입을 만들어서 stream 형태로 만드는 예이다.

Optional<String> foo = Optional.ofNullable("foo");
Stream<String> stream = foo.stream();

or

or 메서드는 기존의 orXXX 와 비슷한 메서드이다. or 메서드 경우에는 Optional을 다시 리턴한다. 메서드 형태는 다음과 같다.

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)

Supplier에 Optional 타입을 받고 Optional을 리턴한다.

Optional<String> foo = Optional.ofNullable("foo");
Optional<String> bar = foo.or(() -> Optional.of("bar"));
// Optional[foo]
Optional<String> foo = Optional.ofNullable(null);
Optional<String> bar = foo.or(() -> Optional.of("bar"));
// Optional[bar]

ifPresentOrElse

기존의 ifPresent(Consumer<? super T> action) 메서드 형태에서 조금 확장된 형태이다. ifPresent() 메서드 경우에는 값이 있을 경우에만 동작하지만 ifPresentOrElse() 메서드 경우에는 값이 없을 경우에도 동작하는 부분이 추가 되었다.
메서드 형태는 다음과 같다.

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

Runnable을 파라마터로 받는 부분이 추가 되었다.

Optional<String> foo = Optional.ofNullable("foo");
foo.ifPresentOrElse(f -> System.out.println(f), () -> System.out.println("bar"));
// foo

Optional<String> foo = Optional.ofNullable(null);
foo.ifPresentOrElse(f -> System.out.println(f), () -> System.out.println("bar"));
// bar 

Process

ProcessHandle 인터페이스가 추가 되었다. Process관련된 정보들을 쉽게 가져올 수 있다.

ProcessHandle processHandle = ProcessHandle.current();
ProcessHandle.Info processInfo = processHandle.info();
long pid = processHandle.pid();
System.out.println(pid);
System.out.println(processInfo.command().get());
System.out.println(processInfo.startInstant().isPresent());
System.out.println(processInfo.totalCpuDuration().isPresent());

간단한 인터페이스이기 때문에 한번씩 해보면 될 듯 싶다.

interface

우리가 아는 java의 interface를 말하는 거 맞다. java8 부터 인터페이스에 구현을 할 수 있게 된건 누구나 아는 사실이다.

interface SomeInterface {
    default void doSomething() {
              System.out.println("blabla");
    }
}

위와 같이 interface에 default라는 키워드를 사용해서 구현을 할 수 있다. java9 부터는 메서드에 private 접근제한을 둘수 있다.

interface SomeInterface {

    private void doSomething(){
              System.out.println("blabla");
    }
}

나쁘지 않다. 하지만 protecteddefault 접근제한은 사용할 수 없다.

Reactive

요즘 대두가 많이 되고 있는 Reactive API가 java9에 추가 되었다. 그 구현체 중 Netflix의 rxJava와 Spring의 Reactor 라는 프로젝트가 그 대표적인 예 이다. 자세한 내용은 rxJava와 Reactor라는 프로젝트를 참고하면 되겠다. 필자의 경우에는 Spring을 자주 사용하니 Spring 관련해서 Reactor를 공부할 듯 싶다. Spring5 에서 정식으로 Reactive를 지원하니 Spring5가 나올 때까지 슬슬 공부하면 될 것 같다. Spring boot는 2.0 부터 지원하니 현재 M1 버전으로 공부해도 될 듯 싶다.
그리고 java9가 나오면 java9 쪽으로 인터페이스를 바꾸지 않을까 생각된다. 그냥 필자 생각이지만..
Reactive 또한 이야기 할 것도 많고 쉬운 내용도 아니니 나중에 좀 더 공부를 한뒤에 글을 작성해보도록 하자.

좀 더 많은 내용이 있겠지만 필자가 알아본 정도는 여기까지이다. java9가 릴리즈 되면 차근차근 좀 더 알아볼 수 있도록 하자. 회사에서 사용하려면 좀 더 안정화 되면 사용해야겠지만 개인적으로는 릴리즈 되면 바로 올려서 사용할 예정이다.
직소가 JCP 리뷰에 통과해서 java9에서 만났으면 좋겠다. java7부터 고생이 많다..

위의 예제들을 간단하게 만들었는데 github에서 살펴보자.