[Java] Optinal

CHOI YUN HO·2022년 3월 21일
0

Java

목록 보기
3/4
post-custom-banner

Optional 이란?


NullPointerException (NPE)

NullPointerException은 가장 흔히 발생하는 에러 중 하나로
null 값을 제대로 처리하지 않았을 때 발생하는 Runtime Error이다.

이를 방지하기 위해서 null을 체크하는 로직을 추가하는 것은
아주 까다롭고 신경이 쓰이는 부분이다.

// (1) itemNames가 null이면 NPE 발생
List<String> itemNames = getItemNames();
itemNames.sort();

// (2) NPE 방지를 위해 null 체크 추가
List<String> itemNames = getItemNames();
if (itemNames != null) {
	itemNames.sort();
}

Java Optional

java 8 이후 null 값을 처리할 수 있도록 Optional 클래스가 추가되었다.

Optional는 null이 저장될 가능성이 있는 변수를 감싸는 Wrapper 클래스로,
참조하더라도 NPE가 발생하지 않도록 도와준다.

Optional 클래스는 여러가지 메서드를 통해 값에 접근하기 때문에
NPE이 발생하지 않으며, null을 처리하는 여러 메서드를 제공한다.

Optional 사용


Optional 생성

  • 값이 Null인 경우
  Optional<String> optional = Optional.empty();
  • 값이 Null이 아닌 경우
  Optional<String> optional = Optional.of("myString");
  • 값이 Null일 수도 있고, 아닐수도 있는 경우
    • optional이 null일 수 있기 때문에 orElse를 통해 안전하게 값을 가져올 수 있다.
  Optional<String> optional = Optional.ofNullable(getMyString());
  String myString = optional.orElse("defaultString");

Optional 메서드

  • filter()

    filter 메서드의 인자로 받은 람다식이 참이면, Optional 객체를 그대로 반환하고,
    거짓이면 Optional.empty()를 반환한다.

	Optional.of("my string").filter(s -> s.contains("string")); // Optional[my string]

	Optional.of("my string").filter(s -> s.contains("hello")); // Optional.empty
  • map()

    Optional 객체의 값을 다른 값으로 변환하는 메서드이다.

	Optional.of("my string").map(String::toUpperCase); // Optional[MY STRING]
  • isPresent()

    Optional 객체의 값이 존재하면 true, null이면 false를 반환하여 판단한다.

	Optional.of("my string").filter(s -> s.contains("string"))
						.isPresent(); // true

	Optional.of("my string").filter(s -> s.contains("hello"))
						.isPresent(); // false
  • ifPresent()

    람다식을 인자로 받아, Optional 객체의 값이 존재하면 그 값에 람다식을 적용하고, null이라면 람다식을 실행시키지 않는다.

	Optional.of("my string").filter(s -> s.contains("string"))
						.ifPresent(System.out::println); // my string 출력

	Optional.of("my string").filter(s -> s.contains("hello"))
						.ifPresent(System.out::println); // 출력 X
  • get()

    Optional 객체의 값을 가져온다. 만약 Optional 객체의 값이 없다면, NoSuchElementException이 발생한다.

	Optional.of("my string").filter(s -> s.contains("string"))
						.ifPresent(System.out::println); // my string 반환

	Optional.of("my string").filter(s -> s.contains("hello"))
						.ifPresent(System.out::println); 
                        // java.util.NoSuchElementException 발생
  • orElse()

    Optional 객체가 null이면, orElse() 메서드에 지정된 기본값이 리턴된다.

	Optional.of("my string").filter(s -> s.contains("string"))
						.orElse("my hello"); // my string 반환

	Optional.of("my string").filter(s -> s.contains("hello"))
						.orElse("my hello"); // my hello 반환
  • orElseGet()

    Optional 객체가 null이면, orElse() 메서드에 인자로 전달된 Supplier 함수의 결괏값을 반환한다.

	Optional.of("my string").filter(s -> s.contains("string"))
						.orElseGet(() -> "my hello"); // my string 반환

	Optional.of("my string").filter(s -> s.contains("hello"))
						.orElseGet(() -> "my hello"); // my hello 반환
  • orElseThrow()

    Optional 객체가 null이면, 인수로 전달된 예외를 발생시킨다.

	Optional.of("my string").filter(s -> s.contains("hello"))
						.orElseThrow(NoSuchFieldError::new); 
                        // java.util.NoSuchElementException 발생

orElse() vs orElseGet()

orElse와 orElseGet 함수 모두 Optioanl 객체가 null일 경우
인자로 전달되는 값을 반환하는 것으로 비슷해 보이는 함수이다.
하지만 큰 차이점이 있다.

public T orElse(T other) {
	return value != null ? value : other;
}

 
public T orElseGet(Supplier<? extends T> supplier) {
	return value != null ? value : supplier.get();
}

✅ orElse : 인자로 값이 전달된다.
✅ orElseGet : 인자로 함수형 인터페이스가 전달된다.

orElse 메서드는 Optional 객체의 값이 있는지 null인지 상관없이 인자를 수행시킨다.
하지만 orElseGet 메서드는 Optional 객체의 값이 null일 경우에만 함수를 실행시킨다.

public void findUserEmailOrElse() {
	String userEmail = "myname@email.com";
	String result = Optional.ofNullable(userEmail)
		.orElse(getUserEmail());

	System.out.println(result);
}

public void findUserEmailOrElseGet() {
	String userEmail = "myname@email.com";
    String result = Optional.ofNullable(userEmail)
    	.orElseGet(this::getUserEmail);

	System.out.println(result);
}

private String getUserEmail() {
	System.out.println("getUserEmail() Called");
    return "defaul@email.com";
}
  • findUserEmailOrElse와 findUserEmailOrElseGet 함수를 호출하였을 경우,
    result 에는 "myname@email.com"이 저장될 것이고
    "myname@email.com"을 출력할 것이다.
  • findUserEmailOrElse 함수에서는 orElse()를 사용하였기 때문에
    userEmail의 값과 무관하게 getUserEmail() 함수를 호출하기 때문에
    "getUserEmail() Called"를 함께 출력할 것이다.
  • 하지만 findUserEmailOrElseGet 함수에서는 userEmail이 null이 아니기 때문에
    orElseGet 메서드 인자를 호출하지 않을 것이다.

따라서 orElse 메서드를 잘못 사용한다면,
큰 시스템 장애를 야기할 수 있으며 orElseGet 메서드보다 비용이 더 크다.

값이 미리 존재하지 않는 경우 가급적 orElseGet 메서드 사용을 추천한다.

profile
가재같은 사람
post-custom-banner

0개의 댓글