Null이란?
- 참조타입 변수에 값이 할당되지 않은 것.
- Production Java Applications에서 NullPointerException는 가장 많이 발생하는 에러라는 결과도 있었다.
- 매번 회피 로직을 넣어야 하기 때문에 번거롭고, 컴파일 타임에 에러가 발견되지 않을 수 있기 때문에 리스크가 있다.
Optional
- Null을 다루는 방법으로 jdk 1.8에서 추가된 객체이다.
- Null을 직접 핸들링하지 않아도 된다.
- 타입만으로 잠재적으로 null일 수 있는 여부를 나타낼 수 있다.
- 체이닝을 통한 중단 및 종단처리가 가능하다.
- oracle docs API Note:
Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance. (https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html)
- 명확하게 "no result"를 나타내야 한다.
- Optional은 메서드 반환 유형으로 사용된다.
- Optional 변수는 null일 수 없으며 항상 Optional인스턴스를 가리켜야 한다.
실제 Optional의 구현 코드를 보면
- 생성자가 전부 private으로 막혀있는 걸 볼 수 있다.
private Optional() {
this.value = null;
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
- 생성자를 막아놓고 empty(), of(), ofNullable() 3가지의 static factory method를 제공해준다
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
API Note에 따르면
- Optional에 null을 넣으면 안되고, 대신 empty()를 이용해야 한다.
- optional의 instance가 singleton임을 보장할 수 없기 때문에
Optional.empty() == 연산자를 사용하면 안되고 반드시 Optional.isPresent()를 사용해야 한다.
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
- of 메서드는 null check 로직이 없다. 고로 null이 들어가면 NullPointerException이 발생할 것이다.
- null 가능성이 있으면 ofNullable()을 사용해야 한다.
optional의 get 메서드
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
get()
- NoSuchElementException를 던지기 때문에 반드시 isPresent()로 null check가 우선 시 되어야 한다.
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
orElse(arg)
- null일 경우 파라미터로 들어오는 인자를 대신 반환한다.
orElseGet(Supplier)
- null일 경우 supplier 가 대신할 값을 반환해준다.
orElse와 orElseGet의 차이
- orElse는 값을 받고 있고 orElseGet은 Supplier 메서드를 받고 있다.
다음과 같이 null일 시 대체 값을 가져오는 메서드를 인자로 넣었을 때
optionalMember.orElse(getReplacementValue());
optionalMember.orElseGet(getReplacementValue());
- orElse는 null이 아닌 경우에도 getReplacementValue()를 호출하고 결과 값을 인자로 갖고 있는 것이고
- orElseGet은 null이 들어오면 함수를 인자로 받아 그 때 getReplacementValue()를 호출할 것이다.
- 만약 orElse에 null이 아니면 호출되지 말아야 하는 메서드가 들어갈 경우 문제가 발생할 것이다.
public T orElseThrow() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
orElseThrow
- null일 경우 건네준 exception을 던진다. Custom Exception과 함께 자주 쓰이는 편리한 메서드이다.
Optional 사용 시 주의할 점
멤버변수에 Optional 선언은 안티패턴이다.
- 위에서 oracle docs에서 봤다시피 optional은 함수의 반환목적으로 만들어져있기 때문이다.
- 자바 객체의 멤버변수(reference type)의 기본값은 null인데 optional에 null을 넣지 말라고 했다!
- 그럼 매번 optional.empty()로 초기화 해주는걸 빠뜨리지 않아야 한다.
- Serializable 구현하지 않았으므로 직렬화 되지 않음
Serializable? 직렬화란
객체를 다른 시스템으로 보내기 위해 byte stream으로 바꾸는 것.
가상 메모리를 사용하기 때문에 논리주소와 실제 주소가 다를 수 있는데, 만약 객체 정보를 그대로 저장하면 가상메모리 주소까지 저장되게 된다. 메모리 주소는 다른 시스템에 전달하는 의미가 없다.
그렇기에 이러한 불필요한 정보를 제외하고 타입,값 정보를 byte형태로 전달하는 것을 직렬화라고 한다.
그리고 Serializable을 구현해야 한다.
Collection대신 null을 반환하지 말아야한다.
- Collections에 많은 메서드들은 이미 null에 안전한 로직들이 구현되어 있고,
null 반환 시 호출하는 함수에서는 또 null check로직이 들어가야 한다. - optional 보다는 Collections.empty()를 활용하자.
optional을 파라미터로 넘기지 않는다.
optional은 공식 문서에서도 메서드 반환 유형으로 사용된다고 했다.
public void function(A a, Optional<B> b) {
if(b.isPresent()){
// check 로직
}
}
- 파라미터로 사용하게 되면 매번 필요하지도 않는 변수를 받는 것이 된다.
- 또 매번 Optional로 isPresent같은 처리를 해야한다.
그럼 어떻게 매번 필요하지 않은 인자를 처리할까?
메서드 오버로딩으로 다형성을 이용해서 처리
public void function(A a) {
//
}
public void function(A a, B b) {
//
}
- 이렇게 하면 각 메서드가 다뤄야할 책임이 줄고 가독성도 좋아질 것이다. 그러면 side effect도 줄어들 것이다.
자바로 웹개발을 하면서 optional을 꼭 쓰게 되는데 이번에 좋은코드 작성법이라는 강의에서 Optional에 대해 다뤄주신 부분이 인상깊어 optional관련 내용을 정리해 보았습니다.
reference : zerobase(실전 프로젝트를 위한 좋은코드 작성법(조덕강사님))
'java' 카테고리의 다른 글
java는 call by reference 일까 call by value 일까? (1) | 2022.07.13 |
---|
댓글