예외 던지기
null 반환 (객체 참조 케이스의 경우)
2가지가 존재했다.
불변 컬렉션이다.Optional<T> 사용Optional.empty()Optional.of(value)Optional.ofNullable(value)예외로 던지는 방식
public static <E extends Comparable<E>> E max(Collection<E> c) {
if (c.isEmpty())
throw new IllegalArgumentException("빈 컬렉션");
E result = null;
for (E e : c)
if (result == null || e.compareTo(result) > 0)
result = Objects.requireNonNull(e);
return result;
}
Optional 을 사용한 방식
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
if (c.isEmpty())
return Optional.empty();
E result = null;
for (E e : c)
if (result == null || e.compareTo(result) > 0)
result = Objects.requireNonNull(e);
return Optional.of(result);
}
Optional 을 반환하는 메서드에서 절대 null 을 반환하지 말아야 한다.Optional 의 도입 취지에 반하는 행위임.Optional.of(value) 에 null 을 넣으면 NPE 가 발생한다.Optional.ofNullable(value) 를 사용해야한다.Optional 을 반환한다.Stream 의 max 연산을 사용하는 경우 Optional 을 쉽게 얻을 수 있다.public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
return c.stream().max(Comparator.naturalOrder());
}
Optional<String> maxed = collection.stream().max(String::compareToIgnoreCase);
기본값의 설정
String lastWordInLexicon = max(words).orElse("단어 없음...");
예외 던지기
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
항상 값이 존재한다고 가정하는 경우
Element lastNobleGas = max(Elements.NOBLE_GASES).get();
지연 초기화
orElseGet 을 사용한다.Optional<ExpensiveObject> optionalExpensive = ...;
ExpensiveObject result = optionalExpensive.orElseGet(() -> createExpensiveObject());
orElseGet 을 사용하는데기본값 객체를 생성하는데, 많은 시간이 소요되는 경우
메모리를 많이 사용하는 큰 객체를 생성해야하는 경우
네트워크 호출이나 DB 조회 등 비용이 많이 드는 연산이 필요한 경우
를 말한다
orElseGet 메서드의 경우 Supplier<T> 를 인수로 받는다.Supplier 를 사용하여 기본값을 생성하기에, 초기 설정 비용을 낮출 수 있다는 장점이 있다.Supplier 는 함수형 인터페이스로서, orElseGet 에서 필요할때( 즉, Optional 이 비어있는 경우에만 ) 기본값을 계산한다.orElse 의 경우에는 Optional 이 비어있든 말든 항상 메서드 호출시점에 이미 평가되는데, 이는 Java 메서드 호출 규칙에 따른 내용이다.orElseGet 은 인자로 함수형 인터페이스를 받기에, Optional 이 없다고 판단된 순간에 지연평가(Lazy evaluation) 을 통하여 필요한 경우에만 기본값 계산 로직이 실행된다. public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
// 비교를 위한 orElse 메서드
public T orElse(T other) {
return value != null ? value : other;
}// 기존 코드
Optional<ProcessHandle> parentProcess = ph.parent();
System.out.println("부모 PID: " + (parentProcess.isPresent() ?
String.valueOf(parentProcess.get().pid()) : "N/A"));
// 개선된 코드 (map 사용)
System.out.println("부모 PID: " +
ph.parent().map(h -> String.valueOf(h.pid())).orElse("N/A")); public static void main(String[] args) {
// 샘플 데이터 생성
List<Optional<String>> streamOfOptionals = Arrays.asList(
Optional.of("Hello"),
Optional.empty(),
Optional.of("World"),
Optional.ofNullable(null),
Optional.of("Java")
);
// Java 8 방식
System.out.println("Java 8 방식:");
Stream<String> resultJava8 = streamOfOptionals.stream()
.filter(Optional::isPresent)
.map(Optional::get);
resultJava8.forEach(System.out::println);
// Java 9 이후 방식
System.out.println("\nJava 9 이후 방식:");
Stream<String> resultJava9 = streamOfOptionals.stream()
.flatMap(Optional::stream);
resultJava9.forEach(System.out::println);
}
리스트의 empty 와 Optional 의 empty 의 개념이 중복된다
- 리스트가 비어 있어도 결국 컬렉션 객체는 존재하는데,
- Optional 은 리스트가 비어 있더라도, 객체는 존재하기에, 존재함을 표현하게 된다.
List 가 비어있음을 나타내면 그만이지.. 내부 타입이 비어있다의 중첩된 사고를 해야하기에 상당히 코드 읽기가 피곤하다.