본문 바로가기

자바

Java Optional

Optional이란

우리가 개발을 할 때 상당히 자주 마주치는 예외 중 하나가 바로 NPE(NullPointerException)일 것이다.

이러한, NPE를 피하기 위해서는 null인지를 체크해주는 로직을 "필히" 넣어주어야 한다.

하지만, if 문을 통해 null을 검사하는 것은 코드의 복잡성을 유발하고 가독성을 해칠 가능성이 크다.

간단한 예를 하나 살펴보자.

만약, 학생의 이름과 나이를 저장할 수 있는 Student 객체가 있다고 하자.

학생은 이름은 필수로 입력해야하지만, 나이의 입력은 선택이라고 한다.

그리고 수업에 참가한다는 AttendClass()라는 메소드가 존재하고 이 메소드의 리턴 값으로 참여한 학생들의 나이들을 출력한다고 하자.

그렇다면, 수업에 참가하는 학생이 있고 학생들의 나이가 입력되어 있을 경우 나이를 출력해야한다. 아니라면 NPE를 마주하게 될 것이다.

그래서 로직은 다음과 같이 복잡하게 이루어진다.

public class OptionalTest {
    public static class Student {
        String name;
        Integer age;

        public Student(String name) {
            this.name = name;
        }

        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    public static void AttendClass(Student ... students) {
        if (students != null) {
            for (Student student : students) {
                if (student.age != null) {
                    System.out.println(student.age);
                }
            }
        }
    }

    public static void main(String[] args) {
        Student gilSSang = new Student("GilSSang", 25);
        AttendClass(gilSSang);
    }
}

그에 따라, Java에서는 위와 같은 상황을 방지하고 NPE를 방지할 수 있는 엄청난 것을 제공한다.

바로 Optional이다.

Optional클래스는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 해준다.

이제 Optional을 왜 사용해야하는지 알아보았으니 실제로 사용하는 방법에 대해서 알아보자.

Optional 생성

Optional은 Wrapper 클래스이기 때문에 빈 값이 올 수도 있어 빈 객체를 생성할 수 있다.

또한, 객체를 Optional객체로 변경시키는 두 가지 방법이 존재한다.

(1) Optional.empty()

Optional<Student> empty = Optional.empty();
System.out.println(empty); // Optional.empty

(2) Optional.of()

Student student = new Student("GilSSang", 25);
Optional<Student> ofStudent = Optional.of(student);
System.out.println(ofStudent.get()); // Student{name='GilSSang', age=25}
  • Optional.of()을 이용하여 변경시킨다.
  • Optional의 디폴트 메소드인 get()을 이용하여 원래 객체를 얻을 수 있다.
Optional<Student> ofStudent = Optional.of(null);// NullPointException
  • Optional.of()는 null에 대해 NPE를 발생시킨다.

(3) Optional.ofNullable()

Student student = new Student("GilSSang", 25);
Optional<Student> ofStudent = Optional.ofNullable(student);
System.out.println(ofStudent.get()); Student{name='GilSSang', age=25}
  • Optional.ofNullable()을 이용하여 변경시킨다.

    Optional.ofNullable()의 경우 null에 대해 Optional.empty를 반환한다.

Optional 연산

Optional은 filter, map 등의 중간 연산을 제공한다. (이는 stream의 사용법과 동일하니 이를 참고하자.)

Optional은 다양한 최종 연산을 제공한다.

(1) isPresent()

Optional<Student> student1 = Optional.empty();
System.out.println(student1.isPresent()); // false

Student student = new Student("GilSSang", 25);
Optional<Student> student2 = Optional.ofNullable(student);
System.out.println(student2.isPresent()); // true
  • 값이 존재하는지 체크한다. (존재할 시 true, 존재하지 않을 시 false)

(3) ifPresent()

Student student = new Student("GilSSang", 25);
Optional<Student> student2 = Optional.ofNullable(student);
student2.ifPresent(st -> AttendClass(st)); // 25
  • 파라미터에 값이 존재할 시 실행될 메소드를 입력한다.
  • 값이 존재한다면 해당 메소드를 실행한다.

(2) ifPresentOrElse()

Student student = new Student("GilSSang", 25);
Optional<Student> student1 = Optional.ofNullable(student);
student1.ifPresentOrElse(st -> AttendClass(st), () -> System.out.println("Null")); // 25

Optional<Student> student2 = Optional.empty();
student2.ifPresentOrElse(st -> AttendClass(st), () -> System.out.println("Null")); // Null
  • 첫 번째 파라미터에 값이 존재할 시 실행될 메소드, 두 번째 파라미터에 값이 존재하지 않을 시 실행될 메소드를 입력한다.
  • 값이 존재할 시 첫 번째 메소드가 실행되고 존재하지 않을 시 두 번째 메소드가 실행된다.

(3) orElse()

Student student = new Student("GilSSang", 25);
Student student1 = Optional.ofNullable(student).orElse(new Student("익명", 0));
System.out.println(student1); // Student{name='GilSSang', age=25}

Student student2 = (Student) Optional.empty().orElse(new Student("익명", 0));
System.out.println(student2); // Student{name='익명', age=0}
  • Optional 객체가 null이든 아니든 반드시 실행된다.
  • null이라면 파라미터의 값으로 설정해준다.

(4) orElseGet()

Student student = new Student("GilSSang", 25);
Student student1 = Optional.ofNullable(student).orElseGet(() -> new Student("익명", 0));
System.out.println(student1); // Student{name='GilSSang', age=25}

Student student2 = (Student) Optional.empty().orElseGet(() -> new Student("익명", 0));
System.out.println(student2); // Student{name='익명', age=0}
  • Optional 객체가 null일때만 실행된다.

(5) orElseThrow()

Student student2 = (Student) Optional.empty().orElseThrow(NoSuchElementException::new);
  • 파라미터에 던질 예외를 설정해준다.
  • null일 경우 설정된 예외를 던져준다.

주의점

  • of()와 ofNullable()을 혼동하지 말자.
  • orElse() 대신 orElseGet()을 사용하자.
  • Optional을 Collection의 원소로 사용하지 말자.
  • Optional을 필드의 타입으로 사용하지 말자.

'자바' 카테고리의 다른 글

JAVA Collection  (0) 2021.10.15
JAVA Lambda, Functional Interface  (0) 2021.10.14
JAVA Exception  (0) 2021.10.13
Java Stream  (1) 2021.10.03
JVM, JRE, JDK  (0) 2021.09.18