java에서는 데이터가 없음을 처리하기 위해 null를 사용할때가 있다. 하지만 잘못 사용하면
NullPointerException(NPE)를 발생시킬수 있다. java8에서 부터는 Optional 클래스를 제공한다.
Optional 클래스를 사용하면 null safe하게 개발 할 수 있다.
Optional 클래스는 값이 존재할 수도 있고, 존재하지 않을 수도 있는 객체를 명시할때 사용한다.
다음은 Optional 클래스 구현 코드중 일부이다.
public final class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
private Optional() {
this.value = null;
}
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
public boolean isPresent() {
return value != null;
}
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
public T orElseThrow() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
}
위 코드를 분석해보면 몇 가지 특징을 알 수 있다.
Optional 클래스를 생성할 때는 해당 유형의 객체를 전달해야 한다.
Optional 클래스를 생성할 때는 3가지 방법을 사용 할 수 있다.
값이 없는 Optional 객체를 생성한다.
Optional<String> emptyExample = Optional.empty();
값이 있는 Optional 객체를 생성한다. 이 때, 전달된 값이 null이면 NullPointerException이 발생한다.
Optional<String> ofExample = Optional.of("ofExample ");
값이 있는 Optional 객체를 생성한다. 이 때, 전달된 값이 null이면 빈 Optional 객체가 생성된다.
String nullValue = null;
Optional<String> ofNullableExample= Optional.ofNullable(value);
Optional 객체가 값을 포함하고 있으면 값을 반환합니다. 그렇지 않으면 NoSuchElementException을 발생시킨다.
Optional<String> ofExample = Optional.of("ofExample ");
String value = ofExample .get();
Optional 객체가 값을 포함하고 있는지 여부를 반환한다.
Optional<String> isPresentExample= Optional.of("isPresent");
if (isPresentExample.isPresent()) {
System.out.println("Value is present.");
}
Optional 객체가 값을 포함하고 있으면 값을 반환하고, 그렇지 않으면 인수로 전달된 값을 반환한다.
Optional<String> orElseExample= Optional.empty();
String value = orElseExample.orElse("Default Value");
Optional 객체가 값을 포함하고 있으면 값을 반환하고, 그렇지 않으면 Supplier 객체의 get() 메서드가 반환하는 값을 반환한다.
Optional<String> optionalString = Optional.empty();
String value = optionalString.orElseGet(() -> "Default Value");
Optional 객체가 값을 포함하고있으면 값을 반환하고,그렇지 않으면 NoSuchElementException또는 직접 설정한 Exception을 반환 한다.
Optional<String> orElseThrowExample= Optional.empty();
String value = orElseThrowExample.orElseThrow(() -> new CustomException("Value not present"));
Supplier는 자바 함수형 인터페이스(Functional Interface) 중 하나로, 매개변수가 없는 메서드를 가지며, T 타입의 객체를 반환하는 메서드를 나타낸다.
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier 인터페이스는 get() 메서드 하나만을 추상 메서드로 가지고 있으며, 이 메서드는 T 타입의 객체를 반환한다. 이 인터페이스는 매개변수가 없는 상황에서 T 타입의 값을 리턴하는 용도로 사용 된다.
Optional<String> optional = Optional.empty();
String value = optional.orElseGet(() -> "Default Value");
위 코드에서는 Optional 객체가 값이 없기 때문에, orElseGet() 메서드가 Supplier 인터페이스의 get() 메서드를 호출한다. 이때 선언된 람다 함수에 의해 "Default Value" 값을 반환하고 T타입에 문자열 타입이 저장된다.