Optional
개발 배경
자바로 프로그램을 개발하면서 NullPointerException
을 겪지 않은 사람은 없다.
public class Person {
private Car car;
public Car getCar() {
return this.car;
}
}
public class Car {
private Insurance insurance;
public Insurance getInsurance() {
return this.insurance;
}
}
public class Insurance {
private String name;
public String getName() {
return this.name;
}
}
Null
을 체크하는 방법
public String getInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance i = car.getInsurance();
if (insurance != null) {
return i;
}
}
}
return "Unknown";
}
public String getCarInsuranceName(Person person) {
if (person == null) {
return "Unknown";
}
Car car = person.getCar();
if (car == null) {
return "Unknown";
}
Insurance i = car.getInsurance();
if (i == null) {
return "Unknown";
}
return i.getName();
}
두 방법 모두 가독성이 좋지 않고 충분히 오류를 일으킬 수 있음.
null
때문에 발생하는 문제
- 에러의 근원
- 코드 가독성 저하
- 의미 없음
- 자바 철학에 위배
Optional 클래스 등장
자바 8은 하스켈과 스칼라의 영향을 받아 java.util.Optional<T>
클래스를 제공한다.
Optional
은 값을 캡슐화한다.
public class Person {
private Car car;
public Optional<Car> getCar() {
return Optional.nullable(this.car);
}
}
public class Car {
private Insurance insurance;
public Optional<Insurance> getInsurance() {
return Optional.nullable(this.insurance);
}
}
public class Insurance {
private String name;
public String getName() {
return this.name;
}
}
Optional
클래스를 사용하면 모델의 의미가 더 명확해진다. 사람은 자동차를 소유할 수도, 안 할 수도 있고, 자동차는 보험이 있을 수도, 없을 수도 있다.
또한, 보험은 이름을 반드시 가지는 것을 보여준다.
Optional 객체 만들기
빈 Optional
Optional<Car> car = Optional.empty();
값을 가진 Optional
Optional<Car> optcar = Optional.of(car);
null이 들어가면 NPE
null을 저장할 수 있는 Optional
Optional<Car> optcar = Optional.ofNullable(car);
Optional Best Practice
Optional 변수에 null 할당하지 말자
// Bad
public Optional<Car> getCar() {
Optional<Car> emptyCar = null;
...
}
// Good
public Optional<Car> getCar() {
Optional<Car> emptyCar = Optional.empty();
...
}
Optional.get
을 사용하기 전에 값이 있는지 확인하자
get()
은 가장 간단한 방법이지만 위험하다. 값이 있으면 해당 값을 반환하지만, 값이 없으면NoSuchElementException
을 발생시킨다.
// Bad
Optional<Car> optCar = Optional.nullable(car);
Car myCar = optCar.get();
// Good
Optional<Car> optCar = Optional.nullable(car);
if(optCar.isPresent()) {
Cart myCar = optCar.get();
}else {
...
}
ifPresent()
- get()
보다는 orElse()
// Bad
public String getInsuranceName(Car car) {
Optional<Insurance> optInsurance = car.getInsurance();
if (optInsurance.isPresent()) {
return optInsurance.get().getName();
} else {
return "Unknown";
}
}
// Good
public String getInsuranceName(Car car) {
Optional<Insurance> optInsurance = car.getInsurance();
return optInsurance.orElse("Unknown");
}
orElseGet()
public double calculate() {
// 보험료 계산
}
// Bad
public int getInsuranceCost(Car car) {
Optional<Insurance> optInsurance = car.getInsurance();
return optInsurance.orElse(calculate());
}
// Good
public String getInsuranceCost(Car car) {
Optional<Insurance> optInsurance = car.getInsurance();
return optInsurance.orElseGet(() -> calculate());
}
값이 있어야
calculate()
실행
orElseThrow
public Car getCar(long personId) {
return carRepository.findCarByPersonId(personId)
.orElseThrow(CarNotFoundException::new);
}
ifPresent
// Bad
Optional<Car> optCar = Optional.nullable(car);
if (optCar.isPresent()) {
System.out.println(optCar.get());
}
// Good
Optional<Car> optCar = Optional.nullable(car);
optCar.ifPresent(System.out::println);
사용하지 말아야 할 곳
필드, 파라미터 등
// Bad
public class Foo {
Optional<String> optName;
public setName(Optional<String> name) {
this.optName = name;
}
}
출처
모던 자바 인 액션 364~388
Java Optional Best Practices
'Java > 모던 자바 인 액션' 카테고리의 다른 글
[모던 자바 인 액션] 리액티브 프로그래밍 (0) | 2023.02.04 |
---|---|
[모던 자바 인 액션] CompletableFuture : 안정적인 비동기 프로그래밍 (0) | 2023.01.20 |
[모던 자바 인 액션] 15장 - CompletableFuture와 리액티브 프로그래밍 컨셉의 기초 (1) | 2022.12.30 |
댓글