java8 버전부터 Optional 클래스가 지원되기 시작했습니다. Optional 내용을 알아볼까요?
Java NPE 예방
자바 프로그램을 작성하다보면 null처리를 해야 할 경우가 많습니다. null 값을 제대로 처리하지 않으면 NullPointerException을 자주 만나게 됩니다. 안정적인 실행을 위해 NPE이 발생하지 않도록 중간중간 null 체크를 해줘야 하는데 if문이 반복적으로 들어가기 때문에 코드가 더러워집니다.
아래와 같은 상황에서 NPE가 발생합니다.
List<String> strs = null;
strs.get(0).toString();
위 코드를 아래와 같이 수정해야 합니다.
List<String> strs = null;
strs.get(0).toString();
if(strs!=null) {
//다음 로직
}
null을 체크하는 로직이 추가된다면 위 코드보다 더욱 더러워질 것 입니다.
Java Optional<T>
java8 버전부터 null값에 대한 처리를 깔끔하게 할 수 있도록 Optional 클래스가 추가되었습니다.
public final class Optional<T> {
private final T value;
}
Optional<T> 클래스는 null 값일 수도 있는 변수를 감싸주는 'Wrapper' 클래스 입니다. Optional 클래스는 'Generic'으로 값의 타입을 지정해줘야 합니다. Optional 클래스는 여러가지 메서드를 통해 value 값에 접근하기 때문에 바로 NPE가 발생하지 않으며, null일 수도 있는 값을 다루기 위해 다양한 메서드를 제공합니다.
Optional.ofNullable(student) //student의 null 체크
.map(Student::getNAME) //name을 가져온다.
.ifPresent(System.out::println); //student의 name이 존재한다면 출력
Java Optional 사용 - 생성
Optional 객체를 생성하기 위해서 아래의 메서드를 사용해야 합니다.
Optional<Object> optional = Optional.of(value);
이 경우에는 value가 null인 경우 NPE가 발생합니다. 반드시 값이 있어야 하는 경우에 of() 메서드를 사용해야 합니다.
Optional<Object> optional = Optional.ofNullable(value);
이 경우에는 value 값이 null일 수 있습니다. value가 null인 경우 Optional.empty()가 리턴됩니다.
Optional<Object> optional = Optional.empty();
empty Optional 객체를 생성합니다. empty Optional 객체는 Optional 객체 자체는 있지만 내부에서 가리키는 참조가 없는 상태입니다. Optional.empty() 객체는 미리 생성되어 있는 싱글톤 인스턴스입니다.
Java Optional 사용 - 중간처리
filter()
filter 메서드는 객체를 가져와서 처리를 하고 Optional 객체를 반환하는 메서드들이 있습니다.
Integer result = Optional.of(1000).filter(num-> (num > 999)).orElse(888); //1000
Integer result = Optional.of(1000).filter(num-> (num > 1111)).orElse(888); //888
Optional<Integer> 객체의 filter 값은 true입니다. true가 아니면 888을 출력하게 됩니다.
map()
Optional 객체의 값을 수정하는 메서드입니다.
String result = Optional.of("hello java").map(String::toUpperCase).orElse("none"); //HELLO JAVA
Java Optional 사용 - 값을 리턴하는 메서드
중간처리 메서드들은 Optional 객체를 리턴해서 체인으로 사용할 수 있는데, 이제부터 소개할 메서드는 체인을 끝내는 메서드입니다.
isPresent()
isPresent() 메서드는 Optional 객체의 값이 null인지 여부, 값이 존재하는지 여부만 판단합니다.
boolean result = Optional.of(1000).isPresent(); //true
ifPresent()
ifPresent() 메서드는 람다식을 인자로 받아, 값이 존재할 때 람다식을 적용해준다. 만약 Optional 객체에 값이 없다면 람다식이 실행되지 않습니다.
Optional.of(null).ifPresent(System.out::println); //실행되지 않는다.
get()
Optional 객체가 가지고 있는 값을 가져옵니다. 만약 Optional 객체에 값이 없다면, NoSuchElementException이 발생합니다.
Optional.of("JAVA").get(); //JAVA 리턴
Optional.ofNullable(null).get(); //NoSuchElementException
orElse()
중간처리 메서드들을 거치면서 Optional 객체가 비어있었다면 orElse() 메서드에 지정된 값이 기본값으로 리턴됩니다.
Integer result = Optional.of(1000).filter(num-> (num > 1111)).orElse(888); //888
orElseGet()
중간처리 메서들을 거치면서 Optional 객체가 비어있었다면 orElseGet() 메서드의 인자로 Supplier(함수형 인터페이스)를 적용하여 객체를 얻어옵니다.
Integer result = Optional.of(1000).filter(num-> (num > 1111)).orElseGet(()->888); //1000
orElse() 메서드는 메서드의 인자를 항상 평가합니다. orElse에 객체를 생성해 Optional 객체가 비어있는 경우 리턴하도록 할 수 있는데, orElse() 메서드의 인자 평가가 항상 발생합니다. 따라서 새로운 객체를 생성하는 비용이 크다면 사용하지 말아야 합니다. orElseGet()메서드는 Optional 객체가 비어있는 경우 Supplier함수가 실행되기 때문에 orElse() 메서드 대신 orElseGet() 메서드를 사용하면 비용을 아낄 수 있습니다.
orElseThrow()
중간처리 메서드들을 거치면서 혹은 원래 Optional 객체가 비어있었다면, Supplier 함수를 실행해 예외를 발생시킵니다.
Integer result = Optional.of(1000).filter(num-> (num > 1111)).orElseThrow(NoSuchElementException::new);
Java9 에서 추가된 메서드
or()
중간처리 메서드로 orElse(), orElseGet()과 비슷합니다. or() 메서드는 Optional 객체를 리턴합니다. 메서드 체인 중간에 Optional.empty()가 되었을 때, Optional.empty() 대신 다른 Optional 객체를 만들어서 넘겨주고 싶을 때 사용합니다.
Optional.of("hello java")
.map(String::toUpperCase)
.or(()->Optional.of("hello java"))
.orElse("null"); //HELLO JAVA
or() 메서드의 인자로는 Supplier 함수형 인터페이스를 넘겨줍니다. Optional.of() 메서드의 인자 값이 null이거나 .map() 체인 메서드의 값이 null을 리턴했을 때 or() 메서드의 Optional.of("hello java")를 리턴합니다.
ifPresentOfElse()
최종적으로 값을 반환하는 메서드입니다. ifPresent() 메서드와 유사하지만 인자를 하나 더 받습니다. 첫 번째 인자로 받은 람다식은 Optonal 객체가 값이 존재하는 경우 실행됩니다. 두 번째 인자로 받은 람다식은 Optional 객체가 비어있을 때 실행됩니다. 첫 번째 인자로는 Consumer 함수형 인터페이스를 받고 두 번째 인자로는 Runnable 함수형 인터페이스를 받습니다.
Optional.of("hello java")
.ifPresentOrElse(str->{
System.out.println("존재! : " + str);
}, ()->{
System.out.println("null");
}); //존재! : hello java
Java10에서 추가된 메서드
orElseThrow()
자바 8에서 사용했던 메서드와 동일하고 인자를 받지않는 메서드가 추가되었습니다.(오버로딩)
기본형에 대한 Optional
int, long, double 형 자료형에 대한 Wrapper 클래스로 OptionalInt, OptionalLong, OptonalDouble 클래스가 제공됩니다. Optonal처럼 사용할 수 있습니다. 하지만 이 경우 Auto Boxing(기본 타입 -> wrapper class), unboxing(wrapper class -> 기본 타입)이 발생하기 때문에 기본형을 사용할 경우 OptionalInt, OptonalLong, OptionalDouble을 사용하는게 좋다.
참조
[Java] Optional 사용법 예제
'programming_kr > java' 카테고리의 다른 글
JAVA8 STREAM(1) (1) | 2022.10.15 |
---|---|
제네릭(generic) (1) | 2022.09.24 |
제어자(modifier) (0) | 2022.09.10 |
람다식(Lambda) (0) | 2022.08.30 |
JAVA 8 interface 변경사항 (0) | 2022.08.27 |
댓글