코드를 작성하다보면 NullPointerException 예외를 접할 수 있는데,
이를 단순히 처리할 수 있도록 도와주는 것이 Optional 클래스이다.
Optional 클래스는 java.util
패키지에 묶여 있고 다음과 같이 정의되어 있다.
public final class Optional<T> extends Object {
private final T value; // 이 참조변수를 통해 저장한다.
. . .
}
Optional은 멤버 value에 인스턴스를 저장하는 일종의 Wrapper(래퍼) 클래스이다.
Optional<String> os1 = Optional.of(new String("Toy1"));
// String 인스턴스를 저장한 Optional 인스턴스 생성, of 메소드 호출
Optional<String> os2 = Optional.ofNullable(new String("Toy2"));
// String 인스턴스를 저장한 Optional 인스턴스 생성, ofNullable 메소드 호출
두 메소드의 차이점은 null 허용 여부에 있다.
ofNullable 인자로는 null을 전달할 수 있다.
반면 of 메솓드에 null을 전달할 경우 NullPointerException이 발생한다.
class StringOptional2 {
public static void main(String[] args) {
Optional<String> os1 = Optional.of(new String("Toy1"));
Optional<String> os2 = Optional.ofNullable(new String("Toy2"));
// 람다식 버전
os1.ifPresent(s -> System.out.println(s));
// 메소드 참조 버전
os2.ifPresent(System.out::println);
}
}
ifPresent의 매개변수 형은 Consumer이다.
public void ifPresent(Consumer<? super T> consumer)
때문에 accept의 구현에 해당하는 람다식 또는 메소드 참조를 ifPresent 호출 시 인자로 전달해야 한다.
map 메소드를 사용하면 if~else 문을 대체할 수 있다.
class OptionalMap {
public static void main(String[] args) {
Optional<String> os1 = Optional.of("Optional String");
Optional<String> os2 = os1.map(s -> s.toUpperCase());
System.out.println(os2.get());
Optional<String> os3 = os1.map(s -> s.replace(' ', '_'))
.map(s -> s.toLowerCase());
System.out.println(os3.get());
}
}
map 메소드의 매개변수 형은 Function이다.
또한 map은 제네릭 클래스에 정의된 제네릭 메소드이다.
public <U> Optional<U> map(Function<? super T, ? extends U> mapper)
따라서 apply의 구현에 해당하는 람다식을 map 호출 시 인자로 전달해야 한다.
이 예제에서는 Optional<String>
의 인스턴스를 대상으로 map 메소드를 호출하므로 T는 String으로 결정이 난 것이다.
map 메소드는 apply 메소드가 반환하는 대상을 Optional 인스턴스에 담아서 반환한다.
map과 flatMap 모두 Optional 인스턴스를 반환하지만,
map은 람다식이 반환하는 내용물을 Optional 인스턴스로 감싸주고
flatMap은 알아서 해주지 않는다.
Optional 인스턴스에 저장된 내용물을 반환하지만,
내용물이 없다면 대신해서 반환할 대상을 정할 수 있다. (= get과 차이점)
class OptionalOrElse {
public static void main(String[] args) {
Optional<String> os1 = Optional.empty();
Optional<String> os2 = Optional.of("So Basic");
String s1 = os1.map(s -> s.toString())
.orElse("Empty");
String s2 = os2.map(s -> s.toString())
.orElse("Empty");
System.out.println(s1);
System.out.println(s2);
}
}
empty 메소드를 호출하면 빈 Optional 인스턴스가 생성되어 반환된다.