java, Optional 활용하기
Optional?
- Optional는 null을 효과적으로 관리하기 위한 인터페이스이다.
if(obj==null)
이란 형태를 뛰어넘어 유연한 코드를 작성하도록 도와준다. - 그러므로 아래와 같이 단순한 null 체크 용도로 사용한다면 Optional의 경우 큰 의미를 가지지 못한다. 오히려 코드만 늘어난다. 기존의 두 줄을 사용한 코드가 Optional을 사용하는 순간 3줄로 늘어난다.
if(optional.isPresent()){
Something something = optional.get();
something.doSomething();
}
if(something != null){
something.doSomething();
}
- Optional은 그것이 제공하는 여러 메서드가 존재한다. 제공하는 메서드를 활용하여 더 좋은 코드를 작성할 수 있다.
Optional의 기본적인 사용 패턴 - orElse
- Optional의 기본적인 사용은 orElse와 함께 한다. null일 경우 적절한 값을 전달하거나 NPE(NullPointerException)가 아닌 다른 예외로 변환하여 전달한다.
Optional<String> op = Optional.empty();
String result = op.orElse("빈 값!");
System.out.println(result); // "빈 값!"
- orElseGet은 함수형 인터페이스를 전달한다. 지연로딩으로 최적화 가능하기 때문에, 해당 값을 생성하는데 많은 리소스를 사용할 경우 사용을 권장한다.
String result = op.orElseGet(longTimeTakenJob()); // Supplier<String>
- 자바의 NPE는 구체적인 메시지가 없어서 로그를 분석할 때 즉각적인 이해가 어렵다. 예외 객체 선택하고 적절한 메시지를 작성하여 이해하기 쉽도록 한다.
MyRequest request;
Optional<String> stringOp = getResult(request);
String value = stringOp.orElseThrow(() -> new IllegalArgumentException("요청에 대한 결과 값이 존재하지 않습니다. 요청 값 : " + request.toString()));
스트림처럼 사용하기 - map과 filter
- Optional을 컬렉션처럼 생각하면 값이 하나만 있거나 혹은 값이 없는 스트림처럼 이해할 수 있다. 스트림의 중간 연산은 스트림에 값이 없더라도 아무런 문제 없이 동작한다.
- 아래는 map을 사용하여 요청에 대한 응답값을 SUCCESS 혹은 ERROR란 응답값으로 반환한다. 만약 값이 없을 경우 NO_RESPONSE로 응답한다.
Optional<String> op = getResponse();
Status result = op
.map(Status::resolveResponse)
.orElse(Status.NO_RESPONSE);
enum Status{
NO_RESPONSE,
ERROR, SUCCESS;
static Status resolveResponse(String s){
if(s.equals("success")) return SUCCESS;
return ERROR;
}
}
Optional을 필드로 사용
- 객체를 구현할 때, 필드의 nullable을 명시하는 것은 중요하다. 해당 객체를 사용할 때 null-safe를 보장하지 못하면 클라이언트 개발자는 보수적으로 코드를 작성할 수밖에 없기 때문이다. 이로 인해 아래와 같이 if가 반복되어 사용되는 장황한 코드가 될 수도 있다.
MyObject o = getMyObject();
String name = o.getName();
if(name!=null){
name = generateName(o, someArgs);
}
- 아래와 같이 null이 가능한 필드는 Optional로 감쌀 수 있다.
class MyObject{
Optional<String> nameOp;
}
- 다만 위의 방식은 직렬화와 관련한 문제로 권장하지 않는다.
- 절충한으로 getter에서 Optional로 감쌀 수 있다.
public Optional<String> getName(){
return Optional.ofNullable(name);
}
2023년 7월 30일 내용 추가 및 수정