좀 더 자세한 내용은 공식문서에서 확인하실 수 있습니다.
java.util.Optional
JAVA의 영원한 숙적인 NullPointerException을 방지해주는. 즉, null인 값을 참조해도 NullPointerException이 발생하지 않도록 값을 래퍼로 감싸주는 타입입니다.
흔하게 볼 수 있는 곳은 Spring Data JPA를 사용할 때 Repository에서 findById()의 반환값은 Optional 타입입니다.
Member member = memberRepository.findById(memberId);
다음과 같이 코드를 작성하면
Optional 타입이라며 IDE가 잡아줍니다.
실제로 보면 반환타입이 Optional 타입인걸 확인할수 있습니다.
Member member = memberRepository.findById(memberId)
.orElseThrow(MemberNotFoundException::new)
반환값이 Optional 타입이기 때문에 이렇게 원하는 member를 찾지 못했을 경우 MemberNotFoundException을 발생시켜, NullPointerException을 미리 방지할 수 있습니다.
Optional 공식문서를 보면 Optional 타입은 여러가지 메소드를 제공합니다. 그 메소드들의 사용법을 하나하나 알아봅시다.
.empty()
기능: 비어있는 Optional 객체를 생성.
리턴값: Optional<T>
// Optional 메소드
Optional<String> empty = Optional.empty();
// 아웃풋
System.out.println(empty.isEmpty()); // true
.of(T value)
기능: 전달된 값으로 새로운 Optional 객체를 생성.
리턴값: Optional<T>
// Optional 메소드
Optional<String> opt = Optional.of("Hello");
// 아웃풋
System.out.println(opt.get()); // Hello
.ofNullable(T value)
기능: 비어있을 수 있고, 아닐 수도 있는 Optional 객체를 생성.
반환값: Optional<T>
// Optional 메소드
String imNull = null;
Optional<String> opt = Optional.ofNullable(imNull);
Optional<String> opt1 = Optional.ofNullable("Hello");
// 아웃풋
System.out.println(opt.isEmpty()); // true
System.out.println(opt1.isEmpty()); // false
.equals(Object obj)
기능: Optional 객체의 값을 비교.
리턴값: boolean
// 인풋
Optional<String> opt1 = Optional.of("Hello");
Optional<String> opt2 = Optional.of("Hello");
Optional<String> opt3 = Optional.of("World");
// Optional 메소드
boolean isEqual1 = opt1.equals(opt2);
boolean isEqual2 = opt1.equals(opt3);
// 아웃풋
System.out.println(isEqual1); // true
System.out.println(isEqual2); // false
.filter(Predicate<? Super T> predicate)
기능: Optional 객체의 값을 조건에 따라 필터링.
리턴값: Optional
// 인풋
Optional<Integer> opt1 = Optional.of(10);
Optional<Integer> opt2 = Optional.of(1);
// Optional 메소드
Optional<Integer> filter1 = opt1.filter(num -> num < 5);
Optional<Integer> filter2 = opt2.filter(num -> num < 5);
// 아웃풋
System.out.println(filter1.isEmpty()); // true
System.out.println(filter2.isEmpty()); // false
.map(Function<? Super T, ? Extends U> mapper)
기능: Optional 객체 내부의 값을 변환하여 결과를 새로운 Optional 객체로 반환.
리턴값: Optional<U>
// 인풋
Optional<String> optional = Optional.of("hello");
// Optional 메소드
Optional<Integer> result = optional.map(s -> s.length());
// 아웃풋
System.out.println(result.get()); // 5
.flatMap(Function<? Super T, ? Extends Optional<? Extends U>> mapper)
기능: 중첩된 Optional
리턴값: Optional<U>
// 인풋
Optional<String> opt = Optional.of("Hello");
// Optional 메소드
Optional<String> flatMappedOpt = opt.flatMap(str -> Optional.of(str.toUpperCase()));
// 아웃풋
System.out.println(flatMappedOpt.get()); // HELLO
map()과 flatMap()을 각각 보면 flatMap()은 map()에 비해 파라미터가 Optional인 것을 확인할 수 있습니다.
즉, map()은 값의 변환이 간단하게 이루어지는 경우에 사용되며, flatMap()은 중첩된 Optional 객체를 다루거나 매핑 함수 자체가 이미 Optional을 반환하는 경우에 사용.
.get()
기본: Optional 객체의 값을 가져옴.
(만약 값이 존재하지 않는 경우, NoSuchElementException이 발생)
리턴값: T
// 인풋
Optional<String> opt1 = Optional.ofNullable("Hello");
Optional<String> opt2 = Optional.ofNullable(null);
// 아웃풋
System.out.println(opt1.get()); // Hello
System.out.println(opt2.get()); // NoSuchElementException
.isEmpty()
기능: Optional 객체가 비어있는지 확인.
리턴값: boolean
// 인풋
Optional<String> opt1 = Optional.ofNullable("Hello");
Optional<String> opt2 = Optional.ofNullable(null);
// 아웃풋
System.out.println(opt1.isEmpty()); // false
System.out.println(opt2.isEmpty()); // true
.isPresent()
기능: Optional 객체가 있는지 확인.
리턴값: boolean
// 인풋
Optional<String> opt1 = Optional.ofNullable("Hello");
Optional<String> opt2 = Optional.ofNullable(null);
// 아웃풋
System.out.println(opt1.isPresent()); // true
System.out.println(opt2.isPresent()); // false
.ifPresent(Consumer<? Super T> action)
기능: Optional 객체가 있다면 내부 연산을 실행
리턴값: void
// 인풋
Optional<String> opt1 = Optional.ofNullable("Hello");
Optional<String> opt2 = Optional.ofNullable(null);
// 아웃풋
opt1.ifPresent(str -> System.out.println(str)); // Hello
opt2.ifPresent(str -> System.out.println(str));
ifPresentOrElse(Consumer<? Super T> action, Runnable emptyAction)
기능: Optional 객체가 있다면 내부 연산을 실행, 없다면 또 다른 내부 연산을 실행
리턴값: void
// 인풋
Optional<String> opt1 = Optional.ofNullable("Hello");
Optional<String> opt2 = Optional.ofNullable(null);
// 아웃풋
opt1.ifPresentOrElse(str -> System.out.println(str),
() -> System.out.println("null")); // Hello
opt2.ifPresentOrElse(str -> System.out.println(str),
() -> System.out.println("null")); // null
.or(Supplier<? Extends Optional<? Extends T>> supplier)
기능: Optional 객체가 비어있다면, 다른 Optional 객체를 반환.
리턴값: Optional<T>
// 인풋
Optional<String> optional1 = Optional.empty();
Optional<String> optional2 = Optional.of("Hello");
// Optional 메소드 사용
Optional<String> result1 = optional1.or(() -> Optional.of("World"));
Optional<String> result2 = optional2.or(() -> Optional.of("World"));
// 아웃풋
System.out.println(result1.get()); // World
System.out.println(result2.get()); // Hello
.orElse(T other)
기능: Optional 객체가 비어있다면, 전달된 기본값 other를 반환.
리턴값: T
// 인풋
Optional<String> optional1 = Optional.empty();
Optional<String> optional2 = Optional.of("Hello");
// Optional 메소드 사용
String result1 = optional1.orElse(null);
String result2 = optional2.orElse(null);
// 아웃풋
System.out.println(result1); // null
System.out.println(result2); // Hello
.orElseGet(Supplier<? Extends T> supplier)
기능: Optional 객체가 비어있다면, 내부 함수를 실행하여 생성된 기본값을 반환.
리턴값: T
orElse()와 다른 점은 함수가 들어갈 수 있다는 점
// 인풋
Optional<String> optional1 = Optional.empty();
Optional<String> optional2 = Optional.of("Hello");
// Optional 메소드 사용
String result1 = optional1.orElseGet(() -> null);
String result2 = optional2.orElseGet(() -> null);
// 아웃풋
System.out.println(result1); // null
System.out.println(result2); // Hello
.orElseThrow()
.orElseThrow(Supplier<? Extends X> exceptionSupplier)
기능: Optional 객체가 비어있다면, Exception을 발생.
반환: T
// 인풋
Optional<String> optional = Optional.empty();
// Optional 메소드 사용
String result = optional.orElseThrow(); // NoSuchElementException 발생
// 인풋
Optional<String> optional = Optional.empty();
// Optional 메소드 사용
String result = optional
.orElseThrow(IllegalArgumentException::new);
// IllegalArgumentException 발생
.stream()
기능: Optional 객체의 값을 Stream으로 변환.
리턴값: Stream<T>
// 인풋
Optional<String> optional = Optional.of("Hello");
// Optional 메소드 사용
optional.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // HELLO
.toString()
기능: Optional의 내부값을 String 문자열로 바꿔 반환.
리턴값: String
// 인풋
Optional<String> optional = Optional.of("Hello");
// Optional 메소드 사용
String stringValue = optional.toString();
// 아웃풋
System.out.println(stringValue); // "Optional[Hello]"
.hashCode()
기능: Optional 객체의 HashCode를 반환.
리턴값: int
// 인풋
Optional<String> optional = Optional.of("Hello");
// Optional 메소드 사용
int hashCode = optional.hashCode();
// 아웃풋
System.out.println(hashCode); // 해시 코드