목차

Java; Optional

🗓️

기본적인 사용법

Optional 객체 만들기

  • Optional 개체 를 만드는 방법에는 여러 가지가 있습니다.
  • 빈 Optional 객체 를 생성하려면 empty() 정적 메서드 를 사용하기만 하면 됩니다 .
@Test
public void whenCreatesEmptyOptional_thenCorrect() {
    Optional<String> empty = Optional.empty();
    assertFalse(empty.isPresent());
}

값의 비교

isPresent(), isEmpty()

  • isPresent() 메서드를 사용 하여 Optional 객체 내부에 값이 있는지 확인했습니다 . 값은 null 이 아닌 값으로 Optional 을 만든 경우에만 존재 합니다. 다음 섹션에서 isPresent() 메서드를 살펴보겠습니다 .
@Test
public void givenNonNull_whenCreatesNonNullable_thenCorrect() {
    String name = "baeldung";
    Optional<String> opt = Optional.of(name);
    assertTrue(opt.isPresent());
}
  • 또한 Java 11부터 isEmpty() 메서드로 반대 작업을 수행할 수 있습니다 .
@Test
public void givenAnEmptyOptional_thenIsEmptyBehavesAsExpected() {
    Optional<String> opt = Optional.of("Baeldung");
    assertFalse(opt.isEmpty());

    opt = Optional.ofNullable(null);
    assertTrue(opt.isEmpty());
}
  • 다른 예시를 들어보자
SoundCard soundcard = ...;
if(soundcard != null){
  System.out.println(soundcard);
}
  • 이 코드 대신 아래의 코드로 대신할 수 있다
@Test
public void scOptional4(){
    Optional<SoundCard> sc = Optional.of(new SoundCard());
    if(sc.isPresent()){
        System.out.println(sc.get());
    }
    sc.ifPresent(System.out::println);
}

ifPresent()

값 넣기

of()

  • of() 메서드에 전달된 인수는 null일 수 없습니다 . 그렇지 않으면 NullPointerException이 발생합니다 .
@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate_thenCorrect() {
    String name = null;
    Optional.of(name);
}

ofNullable()

  • 일부 null 값이 예상되는 경우 ofNullable() 메서드를 사용할 수 있습니다 .
@Test
public void givenNonNull_whenCreatesNullable_thenCorrect() {
    String name = "baeldung";
    Optional<String> opt = Optional.ofNullable(name);
    assertTrue(opt.isPresent());
}
  • 이렇게 하면 null 참조를 전달 하면 예외가 발생하지 않고 빈 Optional 객체를 반환 합니다.

orElse()

  • Optional로 캡슐화된 객체에 orElse구문을 이용하여 null일 경우 새로운 디폴트 값을 할당할 수 있다.
    orElseThrow를 사용하면 null일 경우 특정한 Exception을 발생시키는 것도 가능하다.
@Test
public void whenOrElseWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElse("john");
    assertEquals("john", name);
}

orElseGet()

@Test
public void whenOrElseGetWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
    assertEquals("john", name);
}
  • Optional 또는 Java 8을 처음 접하는 많은 프로그래머에게는 orElse() 와 orElseGet() 의 차이점 이 명확하지 않습니다. 사실 이 두 가지 방법은 기능면에서 서로 겹친다는 인상을 줍니다. 그러나 잘 이해되지 않으면 코드의 성능에 크게 영향을 줄 수 있는 미묘하지만 매우 중요한 차이점이 있습니다. 테스트 클래스에서 인수를 사용하지 않고 기본값을 반환하는 getMyDefault() 라는 메서드를 만들어 보겠습니다 .
public String getMyDefault() {
    System.out.println("Getting Default Value");
    return "Default Value";
}
  • 두 가지 테스트를 보고 orElse() 와 orElseGet()이 겹치는 부분과 다른 부분을 모두 설정하기 위해 부작용을 관찰해 보겠습니다 .
@Test
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);
}
  • 실행결과는 다음과 같습니다.
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);
}
  • 실행 결과는 다음과 같습니다.
Using orElseGet:
Using orElse:
Getting default value...
  • 비어있는 Optional 객체에 대해서, 넘어온 함수형 인자를 통해 생성된 객체를 반환합니다. orElse(T other)의 게으른 버전이라고 보시면 됩니다. 비어있는 경우에만 함수가 호출되기 때문에 orElse(T other) 대비 성능상 이점을 기대할 수 있습니다.

orElseThrow()

  • orElseThrow () 방법은 다음과 OrElse라는 () 및 orElseGet () 와없는 값을 처리하기위한 새로운 방법을 추가한다. 래핑된 값이 없을 때 기본값을 반환하는 대신 예외가 발생합니다.
@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow(
      IllegalArgumentException::new);
}

get()

  • 래핑된 값을 검색하는 마지막 방법은 get() 메서드입니다.
@Test
public void givenOptional_whenGetsValue_thenCorrect() {
    Optional<String> opt = Optional.of("baeldung");
    String name = opt.get();
    assertEquals("baeldung", name);
}
@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() {
    Optional<String> opt = Optional.ofNullable(null);
    String name = opt.get();
}

원시타입 : OptionalInt, OptionalLong, OptionalDouble

  • 원시 타입(primitive type)을 Optional로 사용해야 할 때는 박싱과 언박싱을 거치면서 오버헤드가 생기게 됩니다.
  • 반드시 Optional의 제네릭 타입에 맞춰야 하는 경우가 아니라면, int, long, double 타입에는 OptionalXXX 타입 사용을 고려하는 것이 좋습니다. 이들은 내부 값을 래퍼 클래스가 아닌 원시 타입으로 갖고, 값의 존재 여부를 나타내는 isPresent 필드를 함께 갖는 구현체들입니다.
  • 때문에 기존의 Optional 타입에 사용할 때와 비교하면 박싱과 언박싱에서 생기는 오버헤드를 줄였다는 점에서 장점이 있습니다.

Reference

🏷️