티스토리 뷰

Java

java9#01. 추가된 API 들

LichKing 2018. 8. 15. 16:32

Stream, Optional 관련해서 java9 에서 몇가지 추가된 API들이 있다. 이것들에 대해 포스팅하고자한다.


01. Stream.iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

Stream 클래스의 iterate 메서드는 기존에도 있던거지만 한가지 메서드가 오버로딩되어 추가됐다.

기존 iterate은 무한스트림을 만들수밖에 없었다. 그래서 중간에 스트림을 끊기위해 limit() 를 주로 이용했었다.


Stream.iterate(1, n -> n + 1)
.limit(10)
.forEach(System.out::println);

(중간에 limit(10) 을 호출하지않으면 무한스트림이 된다.)


java9에서는 Predicate을 받는 메서드가 오버로딩되어 Predicate을 이용해 스트림을 제한할 수 있다.


Stream.iterate(1, n -> n <= 10, n -> n + 1)
.forEach(System.out::println);


02. stream.takeWhile(predicate)
해당 조건이 false이면 Stream을 중단하는 중간 연산자(intermediate operation)다. java8 이상의 환경에서 스트림을 자주 사용하는 사람이라면 가끔 이런 연산자의 부재로인해 스트림으로 작성하다가 중간 break를 못걸어서 for문으로 변경한 경험이 있을것이다. 지금은 단순 예제로 보여주지만 그럴땐 요긴하게 쓸수 있을듯하다.


Stream.iterate(1, n -> n + 1)
.takeWhile(n -> n <= 10)
.forEach(System.out::println);


위 예제보다는 이게 그나마 좀 더 사용법을 잘 나타낼것 같아서 추가한다.


List<Integer> integers = Arrays.asList(1, 2, 20, 3, 4);

integers.stream()
.takeWhile(n -> n <= 10)
.forEach(System.out::println);

(결과를 확인해보자. 3,4도 안나온다.)


03. optional.or(Supplier<? extends Optional<? extends T>> supplier)

public static void main(String[] args) {
Optional<String> s = Optional.empty();
String str = s.orElseGet(() -> test());

System.out.println(str);
}

public static String test() {
return null;
}


이런 호출이 있다고 가정해보자. str 변수에 값을 넣고자한다. Optional이 비어있다면 orElseGet() 을 호출한다. 하지만 orElseGet() 에서 호출되는 메서드가 null을 반환할 수도있다(지금 예제에선 100%). 이럴땐 Optional을 사용하는 이득이 반감되게된다. 이럴때 or() 메서드를 사용한다.


public static void main(String[] args) {
Optional<String> s = Optional.empty();
Optional<String> str = s.or(() -> Optional.ofNullable(test()));
}

public static String test() {
return null;
}


시그니처를 보면 알겠지만 orElseGet() 과 같이 Supplier를 받지만 타입이 Optional로 고정되어있다. 한번 더 Optional을 거치게되는것이다.


04. optional.ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

Optional 에는 ifPresent() 라는 메서드가 있어 값이 존재할 경우 실행할 함수를 보낼 수 있다.


List<String> list = new ArrayList<>();

Optional.<String>empty()
.ifPresent(list::add);

(값이 있다면 list에 추가)


하지만 문제는 값이 없을때의 행위를 정의할 수가 없다는 것이다. 값이 없을때를 처리하기위해 Optional의 대표적인 안티패턴으로 코드를 작성할 수 밖에 없었다.


List<String> list = new ArrayList<>();

Optional<String> optionalS = Optional.empty();

if(optionalS.isPresent()) {
list.add(optionalS.get());
}else {
System.out.println("optional is empty");
}

(null 처리 코드랑 똑같아진다)


java9 부터는 이럴때 ifPresentOrElse() 를 사용할 수 있다.


List<String> list = new ArrayList<>();

Optional.<String>empty()
.ifPresentOrElse(list::add, () -> System.out.println("optional is empty"));


05. optional.stream()

Optional 에서 길이가 1인(empty 라면 0) 스트림을 반환하는 메서드이다.

왜 이런게 추가됐을까?


public static void main(String[] args) {
Stream.of(new Person(), new Person())
.map(Person::getStringAsOptional)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
}

static class Person {
Optional<String> getStringAsOptional() {
return Optional.empty();
}
}


스트림을 쓰는데 그 안에서 Optional 이 등장하면 이런류의 코드를 작성하게된다. 이럴때 Optional 에 추가된 stream() 을 호출하면 isPresent -> get 으로 이어지는 Optional 안티패턴을 사용하지않을 수 있다.


Stream.of(new Person(), new Person())
.map(Person::getStringAsOptional)
.flatMap(Optional::stream)
.forEach(System.out::println);


아무래도 Optional 을 사용할때 isPresent() 를 사용하는경우를 최소화 시키려는 느낌이 든다. java9 이상을 사용하면서 isPresent() 를 사용하게된다면 정말 얘를 호출해야하는지 한번 고민해보는게 좋을것 같다.


06. Stream.ofNullable(T t)

크기가 최대 1개짜리인 Stream 객체를 만든다. 물론 t가 null이면 크기는 0이다.

어떤 예제를 작성해야할지 고민이 많았는데 이것 이상이 떠오르지않는다.


public static void main(String[] args) {
Stream.of(new Person(), new Person())
.map(Person::getStringAsOptional)
.flatMap(Stream::ofNullable)
.forEach(System.out::println);
}

static class Person {
String getStringAsOptional() {
return null;
}
}


null 체크를 ofNullable() 을 이용하고있다. 물론 이 코드는 이런식으로 작성할 수 있다.


Stream.of(new Person(), new Person())
.map(Person::getStringAsOptional)
.filter(Objects::nonNull)
.forEach(System.out::println);


java9 을 좀 더 써보면서 어느경우에 저걸 쓸수 있을지 좀 더 고민해봐야할 것 같다.

댓글
댓글쓰기 폼