Study/java

[Java] Optional - 개념 및 사용법

 

Optional은 NullPointerException을 방지하기 위해 많이 사용합니다.

자바로 코딩을 하다 보면 NullPointerException 자주 볼 수 있는데, 그럴 때 Optional을 사용하면 어느 정도 예방이 가능합니다.

 

예를 들면 다음과 같은 null 조건을 Optional로 대체할 수 있습니다.

String nStr = null;
if (nStr == null) {
    nStr = "not null!";
}

//-------

String nStr = null;
String oStr = Optional.ofNullable(nStr).orElse("not null!");
System.out.println(oStr);

두 코드 다 문자열 변수의 값이 null이면 변수에 "not null!"이라는 문자열을 넣어주는 코드입니다.

같은 기능을 하지만 if 구문으로 조건 처리하는 것보다 Optional로 처리하는 것이 더 안전합니다.

 

위의 예제 코드에서는 if 구문이 하나만 있지만 if가 많아지면 코드의 가독성이 많이 떨어집니다.

if (aStr == null) {
    aStr = "not null!";
}
if (bStr == null) {
    bStr = "not null!";
}
if (cStr == null) {
    cStr = "not null!";
}
...
if (zStr == null) {
    zStr = "not null!";
}

이런 코드를

// 예시를 보여주기 위해 작성한 수도코드입니다.
aStr = Optional.ofNullable(aStr).orElse("not null!");
bStr = Optional.ofNullable(bStr).orElse("not null!");
cStr = Optional.ofNullable(cStr).orElse("not null!");
...
zStr = Optional.ofNullable(zStr).orElse("not null!");

이렇게 수정하게 되면 가독성과 NPE 예방이라는 두 마리 토끼를 잡을 수 있습니다.

 

이 Optional 클래스를 어떻게 사용하면 되는지 소개해드리겠습니다.

 

 

 

 

Optional 생성

Optional 객체는 다음과 같이 생성하면 됩니다.

Optional<String> notNull = Optional.of("not null");
Optional<String> nullStr = Optional.ofNullable(null);

System.out.println("notNull = " + notNull);
System.out.println("nullStr = " + nullStr);

생성된 Optional
생성된 Optional

Optional은 of와 ofNullable 메소드로 생성할 수 있습니다.

of는 null이 아닌 객체를 Optional로 만들고 싶을 때, ofNullable은 null 가능성이 있는 객체를 Optional로 만들고 싶을 때 사용합니다.

만약 of에 null이 들어가게 되면 NullPointerException 오류가 발생합니다.

 

그리고 Optional.empty() 메소드로도 Optional 객체를 생성할 수 있습니다.

말 그대로 빈 Optional 객체를 만들어줍니다.

그리고 이 메소드로 만들어진 Optional 객체는 ofNullable 메서드에서 null을 사용했을 때와 동일합니다.

ofNullable 메소드에 null 값이 들어오면 empty 메서드로 객체를 만들어주기 때문입니다.

 

값 가져오기

이렇게 만들어진 Optional 객체에서 값을 가져오는 방법은 다음과 같습니다.

get()

Optional<String> notNull = Optional.of("not null");
Optional<String> nullStr = Optional.ofNullable(null);
Optional<String> empty = Optional.empty();

System.out.println("notNull.get() = " + notNull.get());
System.out.println("nullStr.get() = " + nullStr.get());
System.out.println("empty.get() = " + empty.get());

get 메소드는 Optional 안에 값이 있으면 해당 값을 리턴하고 없으면 NoSuchElementException을 발생시킵니다.

그래서 예제로 보여준 코드에서는 notNull 변수는 값을 리턴 하지만 그다음에는 예외가 발생합니다.

 

orElse()

비어있는 Optional 객체에서 메소드를 가져오는 방법은 다음과 같습니다.

Optional<String> notNull = Optional.of("not null");
Optional<String> nullStr = Optional.ofNullable(null);
Optional<String> empty = Optional.empty();

System.out.println("notNull.orElse(\"notnull -> orElse\") = " + notNull.orElse("notnull -> orElse"));
System.out.println("nullStr.orElse(\"null str\") = " + nullStr.orElse("null str"));
System.out.println("empty.orElse(\"empty\") = " + empty.orElse("empty"));

orElse로 값 가져오기
orElse로 값 가져오기

notNull 문자열은 null이 아니기 때문에 문자열이 그대로 출력되었고, nullStr과 empty 문자열은 orElse에 의해 값이 변경되었습니다.

 

orElse 외에도 orElseGet과 orElseThrow가 있습니다.

orElseGet은 orElse와 같은 기능을 합니다.

하지만 orElse는 대체할 값을 파라미터로 받고, orElseGet은 값이 없을 때 실행할 함수를 받습니다.

Optional<String> notNull = Optional.of("not null");
Optional<String> nullStr = Optional.ofNullable(null);
Optional<String> empty = Optional.empty();

System.out.println(notNull.orElseGet(() -> "not null orElseGet"));
System.out.println(nullStr.orElseGet(() -> "null str"));
System.out.println(empty.orElseGet(() -> "empty"));

orElseGet 사용
orElseGet 사용

두 메소드의 차이는

orElse는 Optional의 값 존재여부에 상관없이 항상 실행되고 orElseGet은 값이 없으면 인자로 받은 함수를 실행합니다.

만약 기본값을 계산하는 로직이 무겁고 복잡하다면 값이 있을 때는 굳이 실행하지 않는 편이 좋습니다.

그럴 때는 orElseGet을 사용해서 값이 없는 경우에만 값을 생성하는 로직을 실행하게 하면 성능 향상에 도움이 될 것입니다.

 

orElseThrow는 Optional에 값이 없는 경우에 예외가 발생합니다.

Optional<String> notNull = Optional.of("not null");
Optional<String> nullStr = Optional.ofNullable(null);

System.out.println(notNull.orElseThrow(() -> new NullPointerException("value is null")));
System.out.println(nullStr.orElseThrow(() -> new NullPointerException("value is null")));

orElseThrow 결과
orElseThrow 결과

값이 있는 경우에는 해당 값을 리턴하고, 없는 경우에는 지정한 예외를 발생시킵니다.

Optional에 값이 없는 이유가 호출자(클라이언트)에게 있을 때, 값을 넣어달라는 내용의 메시지를 보내고 싶거나 null로 인해 내부에 발생할 수 있는 오류에 대한 정보를 알려줄 때 유용하게 사용할 수 있습니다.

 

Optional은 자바 개발에서 피할 수 없는 NullPointerException으로 인해 발생할 수 있는 문제들을 예방하는 데에 도움을 줍니다.

그래서 사용법을 잘 익혀두면 좋습니다.