Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags more
Archives
Today
Total
관리 메뉴

프로그래밍 공부

[자바] #19. Optional 총정리 본문

자바

[자바] #19. Optional 총정리

하 냥 2025. 2. 20. 16:58

📚 Optional 클래스의 기본 개념

Optional<T>는 값이 있을 수도 있고, 없을 수도 있는 컨테이너이다.

null을 반환하는 대신 Optional 객체를 반환하여 null 체크 로직을 줄이고, 함수형 프로그래밍 스타일로 코드를 작성할 수 있다. (NullPointerException을 방지)


📝 Optional 객체 생성 방법

Optional.of(T value)

  • null이 아닌 값을 가지고 Optional 객체 생성
  • null 값이면 NullPointerException 발생
Optional<String> optional = Optional.of("Hello");
System.out.println(optional.get()); // Hello

 

Optional.ofNullable(T value)

  • null 여부에 따라 Optional 생성
  • 값이 null이면 Optional.empty() 반환 (Optional.empty()는  빈 Optional 객체를 반환)
Optional<String> optional = Optional.ofNullable(null);
System.out.println(optional.isPresent()); // false

 

 

 

🔍 Optional 값 확인 및 처리 방법

🎯 isPresent() & isEmpty()

  • 값 존재 여부 확인
Optional<String> optional = Optional.of("Test");
if (optional.isPresent()) {
    System.out.println("값이 존재합니다.");
}

if (optional.isEmpty()) {
    System.out.println("값이 존재하지 않습니다.");
}

⚡ ifPresent(Consumer<? super T> action)

  • 값이 있으면 지정된 동작 수행
optional.ifPresent(value -> System.out.println("값: " + value));

🧱 orElse(T other)

  • 값이 있으면 반환, 없으면 기본값 반환.
String value = optional.orElse("기본값");
System.out.println(value); // Test

🛠️ orElseGet(Supplier<? extends T> supplier)

  • 값이 없으면 람다 표현식으로 기본값 생성
String value = optional.orElseGet(() -> "기본값 생성");
System.out.println(value);

🛡️ orElseThrow(Supplier<? extends Throwable> exceptionSupplier)

  • 값이 없으면 예외 발생
String value = optional.orElseThrow(() -> new IllegalArgumentException("값이 없습니다!"));

🏃 Optional 값을 처리하는 고급 메서드

🔄 map(Function<? super T, ? extends U> mapper)

  • 값이 있으면 함수를 적용하고 결과를 Optional로 반환
Optional<String> optional = Optional.of("hello");
Optional<String> upperOptional = optional.map(String::toUpperCase);
upperOptional.ifPresent(System.out::println); // HELLO

 

추가)

map()만 쓰면 반환 타입은 Optional<String>이다.

하지만 orElse() 를 쓰면 Optional<String>을 String으로 변환하여 변수에 대입 가능하게 해 준다.

orElse()는 Optional<T>를 T로 변환하는 역할!

참고로, orElse()는 Optional<String>에만 쓸 수 있다.

Optional<String> optional = Optional.of("hello");
String str = optional.map(s -> s.toUpperCase()).orElse("Empty"); // orElse 를 추가!
System.out.println(str);

🔀 flatMap(Function<? super T, Optional<U>> mapper)

  • 중첩된 Optional을 평탄화(flatten)하여 다룰 때 사용된다.
  • 즉, Optional<Optional<U>>와 같은 중첩 구조를 Optional<U>로 단순화한다.

💡 map과 flatMap의 차이점

  • map: 값을 변환하고 Optional<U>를 감싼 형태로 반환한다. (Optional<Optional<U>>가 될 수 있음)
  • flatMap: 값을 변환하고 중첩 없이 Optional<U>를 반환한다.
메서드 설명
map(f) 'T → R' 함수 적용 후, 결과를 Optional<R>로 감쌈
flatMap(f) 'T → Optional<R>' 함수 적용 후, Optional<R>로 평탄화
import java.util.Optional;

public class FlatMapExample {

    public static void main(String[] args) {
        Optional<String> name = Optional.of("ChatGPT");

        // map 사용 예제 (중첩 Optional 발생)
        Optional<Optional<String>> mapResult = name.map(FlatMapExample::getUpperCaseName);
        System.out.println("map 결과: " + mapResult); // Optional[Optional[CHATGPT]]

        // flatMap 사용 예제 (중첩 제거)
        Optional<String> flatMapResult = name.flatMap(FlatMapExample::getUpperCaseName);
        System.out.println("flatMap 결과: " + flatMapResult); // Optional[CHATGPT]
    }

    // 대문자로 변환 후 Optional로 반환하는 메서드
    private static Optional<String> getUpperCaseName(String name) {
        return (name == null) ? Optional.empty() : Optional.of(name.toUpperCase());
    }
}

즉, Optional 인스턴스를 클래스의 멤버로 두는 경우에 flatMap()을 유용하게 사용할 수 있다.


🔄 filter(Predicate<? super T> predicate)

  • 조건을 만족하는 값만 Optional로 유지
Optional<String> optional = Optional.of("abc");
optional.filter(value -> value.startsWith("a"))
        .ifPresent(System.out::println); // abc

혹시나 까먹을까 봐.. filter 함수를 람다식이 아니라 함수형 인터페이스를 사용하여 풀어 쓴 예제는 다음과 같다.

Optional<String> optional = Optional.of("abc");

// Predicate를 사용하여 filter 함수형으로 작성
Predicate<String> startsWithA = new Predicate<String>() {
    @Override
    public boolean test(String value) {
	    return value.startsWith("a");
    }
};

optional.filter(startsWithA)
        .ifPresent(System.out::println); // abc

 


🏗️ Optional 실전 사용 예제

💡 null-safe 메서드 체이닝

import java.util.Optional;

class User {
    private String name;
    private Address address;

    public User(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public Optional<Address> getAddress() {
        return Optional.ofNullable(address);
    }
}

class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }

    public Optional<String> getCity() {
        return Optional.ofNullable(city);
    }
}

public class Main {
    public static void main(String[] args) {
        Optional<User> user = Optional.ofNullable(getUser());

        String city = user.flatMap(User::getAddress)
                          .flatMap(Address::getCity)
                          .orElse("도시 정보 없음");

        System.out.println("사용자의 도시: " + city);
    }

    public static User getUser() {
        Address address = new Address("서울");
        return new User("홍길동", address);
    }
}
사용자의 도시: 서울

 

 

💡 getUser() 메서드를 null 반환하도록 변경 시

public static User getUser() {
    return null; // 사용자 정보가 없을 때
}

 

⚡ 출력 결과 (null-safe 처리 확인):

사용자의 도시: 도시 정보 없음

🌟 설명

  • Optional.ofNullable(getUser()): getUser()가 null을 반환하더라도 Optional.empty()를 반환하여 NullPointerException 방지.
  • flatMap(User::getAddress): Optional<Address> 반환.
  • flatMap(Address::getCity): Optional<String> 반환.
  • orElse("도시 정보 없음"): 값이 없을 경우 기본값 제공.

 

❌ 저기서 map을 써 버리면?

String city = user.map(User::getAddress)             // 결과: Optional<Optional<Address>>
                  .map(Address::getCity)             // 결과: Optional<Optional<Optional<String>>>
                  .orElse("도시 정보 없음");         // ❌ 컴파일 에러