Optional을 사용하는 이유는 뭘까

MinSeong Kang·2022년 7월 15일
0

java

목록 보기
4/5

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<T>

java 8 이후부터 null 값을 깔끔하게 처리할 수 있도록 Optional 클래스가 추가되었다.
Optional<T>는 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 메서드 사용을 추천한다.


참고문헌

https://mangkyu.tistory.com/70
https://hbase.tistory.com/212

0개의 댓글