[Java 8] 15. Optional

seony·2023년 5월 7일

java8

목록 보기
15/16

🌱 Optional 이란?

  • Java8에서 Non-Null 값을 표현하기 위해 Optional이 추가되었습니다.
  • 흔히 발생하는 Null Pointer Exception불필요한 null 체크를 피하기 위해 Optional이 생긴 이유기도 합니다.
  • 즉, Optional 클래스는 null을 다루는 것이 안전하지 않은 상황에서 코드의 안정성과 가독성을 높이기 위해 Java 8에서 추가된 클래스입니다.

사용 예시 코드

package com.learn.java.optional;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.Optional;

public class OptionalExample {

	// Optional을 사용하지 않고 학생 이름 가져오기
    public static String getStudentName() {

         Student student = StudentDataBase.studentSupplier.get();
//        Student student = null;
        if (student != null) {
            return student.getName();
        }
        return null;
    }
    
	// Optional을 사용해서 학생 이름 가져오기
    public static Optional<String> getStudentNameOptional() {

        // Optional로 감쌈
        Optional<Student> studentOptional = Optional.ofNullable(StudentDataBase.studentSupplier.get());
//        Optional<Student> studentOptional = Optional.ofNullable(null); // Optional.empty()

        if (studentOptional.isPresent()) {
            return studentOptional.map(Student::getName); // Optional<String>
        }

        return Optional.empty(); // Represents an optional object with no value
    }

    public static void main(String[] args) {

        // Optional 사용하지 않음
        String name = getStudentName();
        if (name != null)
            System.out.println("Length of the student Name : " + name.length());
        else
            System.out.println("Name Not Found");

        // Optional 사용 경우
        Optional<String> stringOptional = getStudentNameOptional();
        if (stringOptional.isPresent()) {
            System.out.println("Length of the student Name : "
                    + stringOptional.get().length()); // String which is Student Name
        } else {
            System.out.println("Name Not Found");
        }

    }
}

🌱 ofNullable(), of(), empty()

  • Optional.ofNullable()
    • 주로 값이 확실하게 존재하는지 알 수 없을 때 사용합니다.
      • 값이 존재 ⭕️: 내부에서 of()를 사용하여 값(value)을 넣어Optional[value]을 return 하고
      • 값이 존재 ❌: 내부에서 empty()를 사용하여 Optional.emptyEMPTY 값을 return 합니다.

Optional.ofNullable(T value)

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
  • Optional..of()
    • Optional.of() 메서드는 파라미터로 전달된 값이 null이 아닌 경우, 해당 값을 가지는 Optional 객체를 생성합니다.
    • 하지만, 만약 파라미터로 null이 전달된 경우, NullPointerException이 발생합니다.
  • Optional.empty()
    • Optional.empty() 메서드를 호출하면, 값이 없는 비어있는 Optional 객체를 생성할 수 있습니다.

예시 코드

package com.learn.java.optional;

import java.util.Optional;

public class OptionalOfEmptyNullableExample {

    public static Optional<String> ofNullable() {
        // 값이 확실하지 않을 때 Optional.ofNullable 사용
        Optional<String> stringOptional = Optional.ofNullable("Hello");
//        Optional<String> stringOptional = Optional.ofNullable(null);
        return stringOptional;
    }

    public static Optional<String> of() {
        // 항상 값이 있을거라고 확신할 때 of()
        Optional<String> stringOptional = Optional.of("Hello");
//        Optional<String> stringOptional = Optional.of(null);
        return stringOptional;
    }

    public static Optional<String> empty() {
        return Optional.empty();
    }

    public static void main(String[] args) {
        System.out.println("ofNullable : " + ofNullable());
        System.out.println("of : " + of());
        System.out.println("empty : "  + empty());
    }
}

결과

ofNullable : Optional[Hello]
of : Optional[Hello]
empty : Optional.empty

🌱 orElse(), orElseGet(), orElseThrow()

  • orElse()

    • orElse()는 Optional 객체에서 값을 가져올 때, Optional 객체가 비어있을 때(default value) 반환할 값을 지정할 수 있는 메서드입니다.
    • Optional 객체가 비어있을 때: orElse() 메서드에 전달된 인자를 반환합니다.
    • Optional 객체가 값이 있을 때 : Optional 객체가 갖고 있는 값을 반환합니다.
  • orElseGet()

    • orElseGet()은 Optional 객체가 비어있을 때(default value) 반환할 값을 Supplier 함수형 인터페이스로 지정할 수 있는 메서드입니다.
    • orElseGet에서 Get부분은 Supplier의 추상 메서드 get을 의미합니다.
  • orElseThrow()

    • orElseThrow()는 Optional 객체가 비었을 때(default value) 예외를 발생시키는 메서드입니다.

예시 코드

package com.learn.java.optional;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.Optional;

public class OptionalOrElseExample {
    
    // orElse
    public static String optionalOrElse() {

//        Optional<Student> studentOptional = Optional.ofNullable(StudentDataBase.studentSupplier.get());
        Optional<Student> studentOptional = Optional.ofNullable(null); // 사실상 Optional.empty()

        // null이면 map안의 메서드를 실행할 수 없으므로 orElse로 넘어감
        String name = studentOptional.map(Student::getName).orElse("Default");
        return name;
    }

    // orElseGet
    public static String optionalOrElseGet() {
//        Optional<Student> studentOptional = Optional.ofNullable(StudentDataBase.studentSupplier.get());
        Optional<Student> studentOptional = Optional.ofNullable(null);


        String name = studentOptional.map(Student::getName).orElseGet(() -> "Default"); // Supplier를 Input으로 받음
        return name;
    }

    // orElseThrow
    public static String optionalOrElseThrow() {
//        Optional<Student> studentOptional = Optional.ofNullable(StudentDataBase.studentSupplier.get());
        Optional<Student> studentOptional = Optional.ofNullable(null); // 예외 발생 시킴


        String name = studentOptional.map(Student::getName)
                .orElseThrow(() -> new RuntimeException("No Data Available"));

        return name;
    }

    public static void main(String[] args) {

        System.out.println("orElse : " + optionalOrElse());
        System.out.println("orElseGet : " + optionalOrElseGet());
        System.out.println("orElseThrow : " + optionalOrElseThrow()); // 예외 발생
    }
}

🌱 ifPresent(), isPresent()

  • ifPresent()
    • ifPresent() 메서드는 Optional 객체가 비어있지 않은 경우, 해당 객체에 대해 지정된 동작(Consumer)을 수행하는 메서드입니다.
  • isPresent()
    • 위의 ifPresent()와 비슷하지만
    • Optional 객체가 비어 있을 경우 : false
    • Optional 객체가 있을 경우 : true 를 반환합니다.

예시 코드

package com.learn.java.optional;

import java.util.Optional;

public class OptionalPresentExample {

    public static void main(String[] args) {

        // isPresent
        Optional<String> optional = Optional.ofNullable("hello Optional");
        System.out.println(optional.isPresent());
        if (optional.isPresent()) {
            System.out.println(optional.get());
        }

        // ifPresent
        optional.ifPresent(System.out::println); // 값이 있다면 input으로 넣은 operation을 실행함.

    }
}

🌱 filter(), map(), flatMap()

  • filter()
    • Optional 객체에 대해 조건식을 적용하여, 조건식이 참인 경우에만 해당 객체를 포함하는 Optional 객체를 반환하는 메서드입니다.
    • Predicate가 조건식을 적용하여 참인 경우에만 해당 객체를 포함하는 Optional 객체를 반환합니다.
  • map()
    • Optional 객체에 대해 지정된 함수를 적용한 후, 적용 결과를 포함하는 Optional 객체를 반환하는 메서드입니다.
  • flatMap()
    • Optional 객체 안의 또 다른 Optional 객체를 평탄화하여, 내부 Optional 객체에 포함된 값을 가져오는 메서드입니다.
    • stream에서의 flatMap()과 차이가 있습니다.

💡 Stream flatMap() 🆚 Optional flatMap()

  • Optional 클래스의 flatMap() 메서드와 Stream 클래스의 flatMap() 메서드는 동일한 이름을 가지고 있지만, 사용 용도가 다릅니다.

    • Optional 클래스의 flatMap() 메서드
      • Optional 객체 안의 Optional 객체를 평탄화하여 내부 Optional 객체에 포함된 값을 가져오는 용도로 사용됩니다.
    • Stream 클래스의 flatMap() 메서드
      • Stream 객체 안에 포함된 각 요소에 대해 다른 Stream 객체를 반환하여, 반환된 Stream 객체를 모두 하나의 Stream 객체로 결합하는 용도로 사용됩니다.

예시 코드

package com.learn.java.optional;

import com.learn.java.data.Bike;
import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.Optional;

public class OptionalMapFlatMapExample {

    // filter
    public static void optionalFilter() {
        // Optional<Student>
        Optional<Student> studentOptional = Optional.ofNullable(StudentDataBase.studentSupplier.get());

        studentOptional.filter(student -> student.getGpa() >= 3.5)
                .ifPresent(System.out::println);
    }

    // map
    public static void optionalMap() {
        Optional<Student> studentOptional = Optional.ofNullable(StudentDataBase.studentSupplier.get());

        if (studentOptional.isPresent()) {
            Optional<String> stringOptional = studentOptional.filter(student -> student.getGpa() >= 3.5)
                    .map(Student::getName); // map을 통해 Student 객체에서 이름만 추출해서 String 객체로 만듭니다.
            System.out.println(stringOptional.get());
        }
    }

    // flatmap
    public static void optionalFlatMap() {
        Optional<Student> studentOptional = StudentDataBase.getOptionalStudent();

        Optional<String> name = studentOptional
                .filter(student -> student.getGpa() >= 3.5) // Optional<Student> 안에 Optional<Bike> 가 들어 있음
                .flatMap(Student::getBike)
                .map(Bike::getName);

        System.out.println(name);
    }


    public static void main(String[] args) {
        optionalFilter();
        optionalMap();
        optionalFlatMap();
    }
}

결과

Student{name='Adam', gradeLevel=2, gpa=4.0, gender='male', activities=[swimming, basketball, volleyball]}
Adam
Optional[Client123]

0개의 댓글