Develop/java

[Optional] Java 유용한 Null 처리

에디개발자 2020. 12. 1. 07:00
반응형

Java 코드를 짜면서 가장 많이 발생하는 에러는 무엇일까?? 바로 NullPointException 입니다. 이 Exception을 최대한 줄일 수 있는 Optional에 대해서 정리하겠습니다.

 

나를 닮았다고 한다....

Java 8부터 Null처리로 Optional이 나왔다. 그러나 개발자들이 기대했던 것과 다르게 만들어져 말이 많았다.... NullPointException를 완벽하게 방어해낼 것이라는 예상과는 다르게 구멍이 있는 기능이다. Optional의 get()같은..

그래서 주의사항이 26가지가 된다. 

 

이 글에서는 주의사항에 다루기 전에 Optional이 무엇인지, 어떤 메소드가 있는지에 대해서 알아보겠습니다.

 

Optional이란?

T 타입의 객체를 Wrapper하는 Wrapper 클래스입니다. 코드를 작성하다보면 메서드를 생성하게 되고 대부분 return 값이 존재합니다. 아래는 간단한 예제입니다.

public StoreVo getStore(int a) {
    StoreVo store = new StoreVo();
    if (a == 0) {
        return null;
    } else {
        store.setId(a);
        return store;
    }
}

 

위 메서드가 공통유틸 클래스에 존재하고 여러 개발자가 메서드를 호출할 상황이 있다고 가정해보고 코드를 작성해보겠습니다.

// 개발자1
int a;
StoreVo store = getStore(a);

if (store != null) {
    int b = store.getId();
    log.info(b);
}



// 개발자2
int a ;
StoreVo store = getStore(a);

int b = store.getId();
log.info(b);

개발자1은 다행히 null처리를 해서 에러가 발생하지 않습니다. 그러나 개발자2는 int a = 0; 일 경우 NullPointException이 발생할 것입니다. 이처럼 개발자들의 실수가 발생할 수 있는 경우를 제외하기 위해서 사용하는 것이 Optional 입니다.

Optional 사용으로 Null 에러가 완벽히 처리되야하는데.. 에러가 발생하는 구멍이 있습니다. 좀 아쉬운 부분입니다. 그래도 Null이 나올수 있다는 것을 한번 더 인지시켜주는 것으로 만족하고 사용해보겠습니다.

 

위 메서드를 Optional을 적용하여 수정해보겠습니다.

public Optional<StoreVo> getStore(int id) {
    StoreVo Store = new StoreVo();
    if (id == 0) {
        return Optional.empty();
    } else {
        store.setId(a);
        return Optional.of(store);
    }
}

 

반환값으로 Optional로 감싸진 객체를 받기 때문에 개발자는 Optional에서 객체를 get하기 전 Null 체크를 해야겠구나라고 인식을 하게됩니다.

int a;
Optional<StoreVo> storeOptional = getStore(a);

if (storeOptional.isPresent()) {      // Optional 내부 객체 empty 아닌 경우
    int b = storeOptioanl.get().getId();    // Optional에서 객체를 가져와 ID를 조회
    log.info(b);
}

이렇게 작성하여 Null 처리를 할 수 있습니다.

 

Optional 메서드

Optional.empty()

Optional.empty();

Optional 빈 객체를 생성합니다.

 

Optional.of();

Optional.of(T);

객체를 Optional로 Wrapper해줍니다.

 

Optional.ofNullable();

Optional.ofNullable(T);

T 타입의 객체가 Null이면 Optional.empty(); 를 return 합니다.

 

optional.get()

Optional<T> optional = Optional.of(t);
T t = optional.get();

Optional로 wrapper된 객체를 가져옵니다.

 

optional.ifPresent()

Optional<T> optional = Optional.of(T);
optioanl.ifPresent(t -> log.info(t));      // T != null 아닐 경우만 실행

Optional로 wrapper된 객체가 null 이 아닐경우에만 실행됩니다.

 

optional.isPresent();

Optional<T> optional = Optional.of(T);
if(optional.isPresent) {    // null이 아닐경우
    // logic
}

Optional로 wrapper된 객체 null 체크를 합니다.

 

optional.orElse();

Optional<T> optional = Optional.of(t);
optStr.orElse(Store::new);	// null 이면 Store 객체 반환

Optional로 wrapper된 객체가 null이면 orElse() 값을 반환

null이던 null이 아니던 orElse() 값을 실행합니다. 위 예제에서는 무조건 new Store()를 실행

 

optional.orElseGet();

Optional<T> optional = Optional.of(t);
optStr.orElseGet(Store::new);	// null 이면 Store 객체 반환

Optional로 wrapper된 객체가 null이면 orElseGet() 값을 반환

null일 경우에만 orElseGet() 값을 실행합니다.

 

optioanl.orElseThrow();

Optional<T> optional = Optional.of(t);
optStr.orElseThrow(() -> new Exception("data is null"));	// null 이면 Exception 반환

Optional로 wrapper된 객체가 null이면 orElseThrow() Exception을 반환

 

OptionalInt, OptionalLong, OptionalDouble

Integer integer;
OptionalInt optionalInt = OptionalInt.of(integer);
optionalInt.getAsInt();

// OptionalLong, OptionalDouble 도 동일

Optional<T>, OptionalInt의 차이

  • Generic하게 받느냐 Integer 타입만 받으냐 차이
  • Generic
    • .of()  : boxing
    • .get() : unboxing
  • Integer
    • .of() : boxing X
    • getAsInt() : unboxing X

 

Optional을 유용하게 쓰면 NullPointException을 줄일 수 있을 것입니다! null party에서 벗어나보자!

다음 글에서 Optional 사용시 주의사항 26가지에 대해서 정리하겠습니다.

이미 잘 정리되어있지만 글로 작성하면서 다시 한번 머릿속에 새기기위한 목적으로 작성하겠습니다.

이글을 참조하여 작성할 예정입니다.

반응형