본문 바로가기
Backend/Java

[Java] 자바 8 - Optional 사용법

by 제이동 개발자 2023. 7. 31.
728x90

자바 8 - (1) Optional 사용법

 Optional은 null이 될 수 있는 값을 명확하고 안전하게 처리하기 위해 자바 8 부터 도입된 Wrapper 클래스입니다. 기존 자바에서는 값이 없는 경우에 null을 반환하는 경우가 많았는데 제대로 null 체크를 해주지 않게 되면 NullPointerException이 발생하는 문제가 빈번히 발생하게 되었습니다. Optional은 이러한 문제를 해결하기 위해 도입된 클래스로 값이 있는 경우에는 해당 값을 감싼 Optional이 반환되고, 값이 없는 경우에는 Optional.empty() 라는 특별한 빈 Optional 객체를 반환합니다. 

 

 또한 Optional은 다양한 메서드들을 지원하므로 널 체크를 더욱 간결하고 명확하게 처리하고, 예외 상황을 더욱 세련되게 다룰 수 있어 코드의 가독성이 향상된다는 장점이 있습니다.

 

 

1. 주요 메서드

주요 메서드 설명
 Optional.of(value)  주어진 값으로 Optional 객체를 생성
 단, value가 null이 아닌 경우에 사용한다.
 Optional.ofNullable(value)  주어진 값으로 Optional 객체를 생성
 단, value의 값이 null인 경우 Optional.empty()를 반환한다.
 Optional.empty()  빈(empty) Optional 객체를 반환
 값이 없음을 나타내는 경우에 사용한다.
 isPresent()  Optional 객체에 값이 존재하는지 여부 확인
 값이 존재하면 true, 값이 없으면 false를 반환한다.
 isEmpty()  Optional 객체에 값이 없는지 확인
 !Optional.isPresent() 와 같다.
 ifPresent(Consumer<? super T> consumer)  값이 존재하는 경우 주어진 동작을 수행한다.
 ifPresentOrElse(
      Consumer<? super T> consumer,
      Runnable emptyAction
 )
 값이 존재하는 경우 주어진 Consumer을 수행하고, 값이 없는 경우 주어진 Runnable을 수행한다.
 filter(Predicate<? super T> predicate)  값이 존재하고, Predicate을 만족하는 경우에만 값을 반환한다.
 map(Function<? super T, ? extends U> mapper)  값이 존재하는 경우에 주어진 Function에 따른 결과 값으로 새로운 Optional을 반환한다.
 or(Supplier<? extends Optional<? extends T>> supplier)  값이 존재하는 경우에는 해당 값을 반환하고, 값이 없는 경우에는 주어진 Supplier로부터 새로운 Optional을 반환한다.
 get()  Optional 객체에 포함된 값을 가져온다.
주의할점은 'isPresent()'로 값의 존재 여부를 판단해야 한다.
 orElse(T orther)  값이 존재하는 경우 해당 값을 반환하고, 값이 없는 경우 주어진 기본 값을 반환한다.
 값이 존재 여부에 상관없이 항상 실행된다.
 orElseget(Supplier<? extends T> orther)  값이 존재하는 경우 해당 값을 반환하고, 값이 없는 경우 주어진 Supplier로부터 기본 값을 가져온다.
 값이 존재하지 않은 경우에만 실행된다.
 orElseThrow(Supplier<? extends X> exceptionSupplier)  값이 존재하는 경우 해당 값을 반환하고, 값이 없는 경우에는 주어진 Supplier로부터 예외를 발생시킨다.

 

1-1. Optional.of(value)

 주어진 값으로 Optional 객체를 생성합니다. 단, 주의할 점은 'value' 값이 절대 null이 아닌 경우에 사용되는 메서드입니다. 만약 'value' 값이 null 인 경우에는 NullPointerException이 발생하게 됩니다.

// ✨ "Hello" 문자열을 값으로 갖는 Optional 객체 생성
Optional<String> optionalValue = Optional.of("Hello");

// ❗ 인자로 null을 넘겨줄 경우 NullPointerException 발생
Optional<String> optionalNullValue = Optional.of(null);

 

1-2. Optional.ofNullable(value)

 주어진 값으로 Optional 객체를 생성합니다. Optional.of(value)와 달리 'value' 값이 null일 수 있는 경우 사용하는 메서드입니다. 만약 null인 경우 Optional.empty()를 반환합니다.

// ✨ "Hello" 문자열을 값으로 갖는 Optional 객체 생성
Optional<String> optionalName = Optional.ofNullable("Hello");

// ✨ Optional.empty() 객체 생성
Optional<String> optionalName = Optional.ofNullable(null);

 

1-3. Optional.empty()

 값이 없음을 나타내는 빈(empty) Optional 객체를 반환합니다. 

// ✨ String 값이 없는 빈 Optional 객체 생성
Optional<String> emptyOptional = Optional.empty();

 

1-4.
isPresent()
isEmpty()

 Optional 객체에 값이 존재하는지 여부를 확인할 때 사용하는 메서드입니다. 'isPresent()' 메서드는 값이 존재하면 true, 값이 없으면 false를 반환하고, 'isEmpty()'는 그 반대로 값이 존재하지 않으면 true, 값이 존재하면 false를 반환합니다.

if (optionalValue.isPresent()) {
    // 값이 있을 경우 분기처리
    ...
}
if (optionalValue.isEmpty()){
    // 값이 없을 경우 분기처리
    ...
}

 

1-5. ifPresent(Consumer<? super T> consumer)

 Optional 객체에 값이 존재하는 경우에만 주어진 Consumer를 수행합니다. 따라서 값이 있으면 동작이 실행되고, 값이 없으면 동작을 수행하지 않습니다.

Optional<String> optionalValue = Optional.of("Hello");
// ✨ 값이 존재하므로 ifPresent 수행
optionalValue.ifPresent(value -> System.out.println("Value: " + value));

Optional<String> optionalNullValue = Optional.ofNullable(null);
// ✨ 값이 null이므로 ifPresent 수행하지 않음
optionalNullValue.ifPresent(value -> System.out.println("Value: " + value));

 

1-6. ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

 Optional 객체에 값이 존재하는 경우에는 주어진 Consumer을 수행하고, 값이 없는 경우에는 주어진 Runnable을 수행합니다.

Optional<String> optionalValue = Optional.ofNullable("Hello");
optionalValue.ifPresentOrElse(
    value -> System.out.println("Value: " + value),   // ✨ 값이 존재하므로 Consumer 수행
    () -> System.out.println("Value is not present.")
);

Optional<String> optionalNullValue = Optional.ofNullable(null);
optionalNullValue.ifPresentOrElse(
    value -> System.out.println("Value: " + value), 
    () -> System.out.println("Value is not present.") // ✨ 값이 존재하지 않으므로 Runnable 수행
);

 

1-7. filter(Predicate<? super T> predicate)

 Optional 객체에 값이 존재하고 주어진 Predicate을 만족하는 경우에 기존 Optional을 반환합니다. 그렇지 않은 경우에는 Optional.empty()를 반환합니다.

Optional<Integer> optionalNumber = Optional.of(10);
Optional<Integer> filteredNumber = optionalNumber.filter(number -> number > 5);
filteredNumber.ifPresent(value -> System.out.println("Filtered Value: " + value));


Optional<Integer> optionalNumber2 = Optional.of(3);
// ❗ Predicate에 만족하지 않으므로 Optional.empty()를 반환한다.
Optional<Integer> filteredNumber2 = optionalNumber2.filter(number -> number > 5);
// ❗ Optional에 값이 없으므로 실행되지 않는다.
filteredNumber2.ifPresent(value -> System.out.println("Filtered Value: " + value));

 

1-8. map(Function<? super T, ? extends U> mapper)

 Optional 객체에 값이 존재하는 경우에 주어진 Function에 따른 결과 값으로 새로운 Optional을 반환합니다.

Optional<String> optionalName = Optional.of("John");
Optional<Integer> optionalLength = optionalName.map(name -> name.length());

optionalLength.ifPresent(length -> System.out.println("Name Length: " + length));

 

1-9. or(Supplier<? extends Optional<? extends T>> supplier)

 Optional 객체에 값이 존재하는 경우에는 해당 값을 반환하고, 값이 없는 경우에는 주어진 Supplier로부터 새로운 Optional 객체를 반환합니다.

Optional<String> optionalName = Optional.ofNullable(null);
Optional<String> orOptional = optionalName.or(() -> Optional.of("new optional object"));

orOptional.ifPresent(value -> System.out.println("Value: " + value));

 

1-10. get()

 Optional 객체에 값을 가져오는 메서드입니다. 단, 주의할 점은 값이 없는 경우 NoSuchElementException이 발생하기 때문에 'get()' 메서드를 호출하기 전에 'isPresent()'로 값의 존재 여부를 확인해야 합니다.

Optional<String> optionalValue = Optional.of("Hello");
// ✨ 값이 존재하는지 여부 확인
if (optionalValue.isPresent()) {
    // ✨ 값이 존재하면 가져오기
    String value = optionalValue.get();
    System.out.println("Value: " + value);
}

 

 참고로 위와 같은 코드는 권장하는 코드 스타일이 아닙니다. 일반적인 null 체크하는 것과 다름없는 코드로 Optional의 장점을 활용하지 못한 코드입니다. 따라서 특별한 분기처리를 해야 하는 경우가 아니라면 'orElseGet', 'orElse', 'orElseThrow' 등 Optional에서 제공하는 메서드들을 이용하여 가독성 좋고 유연한 코드를 작성하는 것이 좋습니다.

Optional<String> optionalValue = ..;

// ✨ 값이 없을 경우 Default 값 반환
optionalValue.orElse("Default value");
optionalValue.orElseGet(this:getDefaultValue);

// ✨ 값이 없을 경우 예외 발생
optionalValue.orElseThrow(Exception::new);

 

1-11. orElse(T other)

 Optional에 값이 존재하는 경우에는 해당 값을 반환하고, 값이 없는 경우에는 주어진 기본 값을 반환합니다. 주의할 점은 Optional에 값이 존재하는 경우에도 'orElse' 메서드가 실행되어 기본 값을 무조건 생성합니다. 따라서 비용이 큰 기본 값을 얻을 때 사용하기보다는 단순한 기본 값을 얻고자 할 때 사용하는 것이 일반적입니다.

public class Coffee {
    ...
    public Coffee() {
        System.out.println("default constructor call");
    }
    ...
}
@Test
void optional_get_test() {
    Optional<Coffee> test = Optional.ofNullable(new Coffee()); // 기본 생성자 호출
    // ✨ Optional에 값이 있음에도 기본 생성자가 호출된다.
    Coffee coffee1 = test.orElse(new Coffee());
}

// 결과 - 기본 생성자 2번 호출
default constructor call
default constructor call

 

1-12. orElseGet(Supplier<? extends T> orther)

 Optional에 값이 존재하는 경우에는 해당 값을 반환하고, 값이 없는 경우에는 주어진 Supplier로부터 기본 값을 가져옵니다. 'orElse' 메서드와는 달리 Optional에 값이 없는 경우에만 Supplier가 호출되어 기본 값을 생성합니다. 따라서 기본 값의 비용이 큰 연산이거나, 외부 자원에 의존하는 경우 사용하는 것이 유용합니다.

public class Coffee {
    ...
    public Coffee() {
        System.out.println("default constructor call");
    }
    ...
}
@Test
void optional_get_test() {
    Optional<Coffee> test = Optional.ofNullable(new Coffee()); // 기본 생성자 호출
    // ✨ 기본 생성자 호출하지 않음
    Coffee coffee = test.orElseGet(Coffee::new);
}

// 결과 - 기본 생성자 1번 호출
default constructor call

 

1-13. orElseThrow(Supplier<? extends X> exceptionSupplier)

 Optional에 값이 존재하는 경우에는 해당 값을 반환하고, 값이 없는 경우에는 주어진 Supplier로부터 예외를 발생시킵니다.

Optional<Coffee> coffeeOp = Optional.ofNullable(null);
// ✨ Optional의 값이 null이므로 예외 발생
Coffee coffee = coffeeOp.orElseThrow(Exception::new);

 

 


 

관련 포스팅

728x90