Optional

하마·2025년 2월 26일

Java

목록 보기
3/8

Optional이란?


  • null 을 안전하게 다루게 해주는 객체
  • NullPointerException 을 방지할 수 있음
  • Optional<T>null 이 올 수 있는 값을 감싸는 Wrapper 클래스

Optional이 필요한 이유


예제 코드

  • camp.getStudnet()null 을 반환할 가능성이 있는 메소드
  • 학생이 없는 경우 null 을 반환하게 되고, NullPointerException 예외가 발생함
public class Student {
    // 속성
    private String name;
    
    // 생성자
    
    // 기능
    public String getName() {
        return this.name;
    }
}
public class Camp {
    // 속성
    private Student student;

    // 생성자
    
    // 기능: ⚠️ null 을 반환할 수 있는 메서드
    public Student getStudent() {
        return student;
    }
    
    public void setStudent(Student student) {
		    this.student = student;    
    }
}
public class Main {
    public static void main(String[] args) {
        Camp camp = new Camp();
        // ⚠️ student 에는 null 이 담김
        Student student = camp.getStudent();
        // ⚠️ 아래 코드에서 NPE 발생! 
        // 런타임 예외이므로 컴파일러가 잡아주지 않음
        String studentName = student.getName();
        System.out.println("studentName = " + studentName);
    }
}

NULL을 직접 처리하는 방법

public class Main {
    public static void main(String[] args) {
        Camp camp = new Camp();
        Student student = camp.getStudent();

        String studentName;
        // ⚠️ 가능은하지만 현실적으로 어려움
        if (student != null) {
            studentName = student.getName();
        } else {
            studentName = "등록된 학생 없음"; // 기본값 제공
        }

        System.out.println("studentName = " + studentName);
    }
}

물론 가능한 방법이지만
모든 코드에서 null 이 발생할 가능성을 미리 예측하고 처리하는 것은 현실적으로 어려움


Optional 활용하기


Optional (Java Platform SE 8)

  • 값이 있을 수도 있고 없을 수도 있는 컨테이너
  • 메소드 반환 자료형에 선언해서 해당 메소드가 null 이 반환될 가능성을 명시할 수 있음
  • Optional.ofNullable() 을 사용해서 null 이 반환될 수 있는 객체를 감쌈
  • isPresent() 와 같은 기능을 사용해서 안전하게 null 을 처리할 수 있음

1️⃣ isPresent() 활용

  • Optional 내부에 값이 존재할 때 true 반환
  • Optional 내부에 값이 존재하지 않을 때, 즉 null 일 경우 false 반환
public class Camp {
    // 속성
    private Student student;

    // 생성자

    // 기능
    // ✅ null 이 반환될 수 있음을 명확하게 표시
    public Optional<Student> getStudent() {
        return Optional.ofNullable(student);
    }
    
    public void setStudent(Student student) {
		    this.student = student;    
    }
}
public class Main {
    public static void main(String[] args) {
        Camp camp = new Camp();
        
        // isPresent() 활용시 true 를 반환하고 싶을때 활용
        // Optional 객체 반환받음
        Optional<Student> studentOptional = camp.getStudent();

        // Optional 객체의 기능 활용
        boolean flag = studentOptional.isPresent(); // false 반환
        if (flag) {
            // 존재할 경우
            // ✅ 안전하게 Student 객체 가져오기
            Student student = studentOptional.get();
            String studentName = student.getName();
            System.out.println("studentName = " + studentName);

        } else {
            // null 일 경우
            System.out.println("학생이 없습니다.");
        }
    }
}

2️⃣ orElseGet() 활용

  • 값이 없을 때만 기본값을 제공하는 로직을 실행하는 메소드
public class Camp {
    // 속성
    private Student student;

    // 생성자

    // 기능
    // ✅ null 이 반환될 수 있음을 명확하게 표시
    public Optional<Student> getStudent() {
        return Optional.ofNullable(student);
    }
}
public class Main {
    public static void main(String[] args) {
        Camp camp = new Camp();
        // ✅ Optional 객체의 기능 활용 (orElseGet 사용)
        Student student = camp.getStudent()
                              .orElseGet(() -> new Student("미등록 학생"));

        System.out.println("studentName = " + student.getName());
    }
}

isPresent() 와 비슷한 기능을 제공하지만
람다식을 활용하기 때문에 간결한 코드 작성 가능



Optional API


Optional.of()

어떤 데이터가 절대 null 이 아니라면 Optional.of() 로 생성할 수 있다.
이 경우에 데이터가 null 이 들어오면 NullPointerException 예외가 발생한다.

// Optional의 value는 절대 null이 아니다.
Optional<String> optional = Optional.of("MyName");

Optional.ofNullable()

어떤 데이터가 null 일 수도 있고 아닐 수도 있으면 Optional.ofNullable() 로 생성할 수 있다.
orElse()orElseGet() 메소드를 이용해서 데이터를 안전하게 가져올 수 있다.

// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("anonymous"); // 값이 없다면 "anonymous" 를 리턴

사용법 예시 (1)

아래와 같이 우편번호를 조회하는 null 검사 코드가 있다고 하자.
위 코드는 null 검사 때문에 복잡하다.

public String findPostCode() {
    UserVO userVO = getUser();
    if (userVO != null) {
        Address address = user.getAddress();
        if (address != null) {
            String postCode = address.getPostCode();
            if (postCode != null) {
                return postCode;
            }
        }
    }
    return "우편번호 없음";
}

Optional 을 사용하면 다음과 같이 표현할 수 있다.

public String findPostCode() {
    // 위의 코드를 Optional로 펼쳐놓으면 아래와 같다.
    Optional<UserVO> userVO = Optional.ofNullable(getUser());
    Optional<Address> address = userVO.map(UserVO::getAddress);
    Optional<String> postCode = address.map(Address::getPostCode);
    String result = postCode.orElse("우편번호 없음");

    String result = user.map(UserVO::getAddress)
					.map(Address::getPostCode)
					.orElse("우편번호 없음");
}

스트림(Stream)메소드 참조 연산자(::) 를 함께 사용하면 코드가 많이 짧아지고, 코드 가독성이 높아진다.

사용법 예시 (2)

이름을 대문자로 변경하는 코드에서 NullPointerException 을 처리해준다고 하자.

String name = getName();
String result = "";

try {
    result = name.toUpperCase();
} catch (NullPointerException e) {
    throw new CustomUpperCaseException();
}

Optional 을 사용하면 다음과 같이 표현할 수 있다.

Optional<String> nameOpt = Optional.ofNullable(getName());
String result = nameOpt.orElseThrow(CustomUpperCaseExcpetion::new)
					.toUpperCase();

스트림(Stream)메소드 참조 연산자(::) 를 함께 사용하면 코드가 많이 짧아지고, 코드 가독성이 높아진다.


정리


Optional 은 null 또는 값을 감싸서 NullPointerException 로부터 부담을 줄이기 위해 등장한 Wrapper 클래스이다.

Optional 은 값을 Wrapping하고 다시 풀고, null 일 경우에는 대체하는 함수를 호출하는 등의 오버헤드가 있으므로
잘못 사용하면 시스템 성능이 저하된다.

그렇기 때문에 메소드의 반환 값이 절대 null이 아니라면 Optional을 사용하지 않는 것이 좋다.
즉, Optional은 메소드의 결과가 null이 될 수 있으며, null에 의해 오류가 발생할 가능성이 매우 높을 때 반환값으로만 사용되어야 한다.

orElse()orElseGet() 는 큰 차이가 있으니, 차이점을 정확히 이해하고 사용해야 한다.
자세한 정보는 참고자료를 참조하자.


참고자료


Optional이란? Optional 개념 및 사용법 - (1/2)
언제 Optional을 사용해야 하는가? 올바른 Optional 사용법 가이드 - (2/2)
챕터 3-2 : Optional - null을 다루는 법

0개의 댓글