JUST WRITE

Optional 본문

Programing/Java

Optional

천재보단범재 2021. 10. 31. 18:17
이 글은 Baeldung 사이트 'Guide To Java 8 Optional'를 해석, 정리한 글입니다.

 

Optional

Optional

Optional은 NullPointException으로부터 보호하면서 읽기 쉬운 코드로 작성되기 위해 도입되었다.

Java 8 에 java.util.Optional로 도입되었다.

Optional 객체 생성

Optional 객체는 empty, of, ofNullable Method 로 생성할 수 있다.

  • empty -> 비어 있는 Optional 객체 생성
  • of -> Argument로 들어오는 객체로 감싼 Optianl 객체 생성, Null이 들어오면 NullPointerException 발생
  • ofNullable ->  Argument로 들어오는 객체(Null도 상관 없음)로 감싼 Optianl 객체 생성
public void createOptionalObjects() {
    Optional<String> empty = Optional.empty();
    
    String name = "baeldung";
    Optional<String> opt = Optional.of(name);
    
    Optional<String> opt = Optional.of(null); // NullPointerException 발생!!!
    
    String name = "baeldung";
    Optional<String> opt = Optional.ofNullable(name);
}

Optional 객체 Value Check

Optional 객체 저장된 값을 Check 하려면 isPresent, isEmpty Method로 확인할 수 있다.

isEmpty Method는 Java 11부터 도입되었다.

public void checkOptionalValue() {
    Optional<String> opt = Optional.of("Baeldung");
    Sytem.out.print(opt.isPresent()); // true

    opt = Optional.ofNullable(null);
    Sytem.out.print(opt.isEmpty()); // true
}

우리는 간혹 Null Check 하는 것을 놓칠 수 있다.

그래서 ifPresent Method를 활용하여 값이 있을때만 실행되도록 할 수 있다.

@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
    Optional<String> opt = Optional.of("baeldung");
    opt.ifPresent(name -> System.out.println(name.length()));
}

/*
* ifPresent를 쓰면 아래 코드 따로 작성할 필요 X
*/
if(name != null) {
    System.out.println(name.length());
}

orElse vs orElseGet vs orElseThrow

orElse vs orElseGet

두 Method 모두 Optional 객체에 값이 없을 때 Default 값을 return 해준다.

public void checkProcessOrElse() {
    // orElse
    String nullName1 = null;
    String name1 = Optional.ofNullable(nullName1).orElse("john");
    System.print.out(name1); // john
    
    // orElseGet
    String nullName2 = null;
    String name2 = Optional.ofNullable(nullName2).orElseGet(() -> "john");
    System.print.out(name2); // john
}

 

다만 두 Method는 아래와 같은 차이점이 있다.

  • orElse -> Optional 객체의 값에 상관없이 Call
  • orElseGet -> Optional 객체의 값이 비어 있을때만 Call
public String getMyDefault() {
    System.out.println("Getting Default Value");
    return "Default Value";
}

public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
    String text = null;

    String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Default Value", defaultText);

    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Default Value", defaultText);
}
// null일때는 orElse, orElseGet 모두 실행(print)
Getting default value...
Getting default value...


@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
    String text = "Text present";

    System.out.println("Using orElseGet:");
    String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Text present", defaultText);

    System.out.println("Using orElse:");
    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Text present", defaultText);
}
// 값이 있을때 orElse만 실행(print)
Using orElseGet:
Using orElse:
Getting default value...

orElseThrow

Optional 객체에 값을 return 해주는데 값이 없으면 사용자가 정의한 Exception을 발생한다.

Java 10에서는 Argument 없는 orElseThrow가 제공되었다.

Argument 없는 Method 사용 시 NoSuchElementExeption을 발생한다.

@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow(
      IllegalArgumentException::new);
}

// No Argument orElseThrow Method
@Test(expected = NoSuchElementException.class)
public void whenNoArgOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow();
}

fiter

filter Method는 보통 정의된 규칙에 따른 값을 선별할 때 사용한다.

예를 들어 Email, Password Format을 선별할 때 유용하다.

보통 map Method와 함께 사용한다.

public class Modem {
    private Double price;

    public Modem(Double price) {
        this.price = price;
    }
    // standard getters and setters
}

// No Optional
public boolean priceIsInRange1(Modem modem) {
    boolean isInRange = false;

    if (modem != null && modem.getPrice() != null 
      && (modem.getPrice() >= 10 
        && modem.getPrice() <= 15)) {

        isInRange = true;
    }
    return isInRange;
}

// Optional filter 활용
public boolean priceIsInRange2(Modem modem2) {
     return Optional.ofNullable(modem2)
       .map(Modem::getPrice)
       .filter(p -> p >= 10)
       .filter(p -> p <= 15)
       .isPresent();
 }

Optional 잘못된 사용

Optional을 Method Parameter에 정의해서 사용하면 안 된다.

Parameter에 정의해서 사용하게 되면 NullPointerException이 발생할 상황이 생긴다.

그렇게 된다면 Optional 사용할 필요가 없다.

public static List<Person> search(List<Person> people, String name, Optional<Integer> age) {
    // Null checks for people and name
    return people.stream()
            .filter(p -> p.getName().equals(name))
            .filter(p -> p.getAge().get() >= age.orElse(0))
            .collect(Collectors.toList());
}

// Optional Argument에 Null 들어가면 NullPointerException 발생!!!
someObject.search(people, "Peter", null);

위 Method를 아래와 같이 2가지 방법으로 개선하는 게 좋다.

1. Return으로 Optional 활용

public static List<Person> search(List<Person> people, String name, Integer age) {
    // Null checks for people and name
    final Integer ageFilter = age != null ? age : 0;

    return people.stream()
            .filter(p -> p.getName().equals(name))
            .filter(p -> p.getAge().get() >= ageFilter)
            .collect(Collectors.toList());
}

2. Overloading 활용

public static List<Person> search(List<Person> people, String name) {
    return doSearch(people, name, 0);
}

public static List<Person> search(List<Person> people, String name, int age) {
    return doSearch(people, name, age);
}

private static List<Person> doSearch(List<Person> people, String name, int age) {
    // Null checks for people and name
    return people.stream()
            .filter(p -> p.getName().equals(name))
            .filter(p -> p.getAge().get().intValue() >= age)
            .collect(Collectors.toList());
}

Optional and Serialization

field Type으로 Optional를 활용하는 것 추천하지 않는다.

Serializtion 객체에 Optional field를 사용하면 NotSerializableException이 발생할 수 있다.

해당 사항은 따로 글을 작성하여 설명하려 한다.

[참고사이트]

728x90
반응형

'Programing > Java' 카테고리의 다른 글

JVM  (0) 2021.11.27
Serialization  (0) 2021.11.20
Log4j vs Logback  (0) 2021.10.26
Interface vs Abstract  (0) 2021.10.21
Generic  (0) 2021.10.15
Comments