본문 바로가기
Book

[독서 기록] 모던 자바 인 액션 11장 null 대신 Optional 클래스

by Renechoi 2023. 1. 11.

11장 null 대신 Optional 클래스 

 

보수적인 null 처리법 

public String getCarInsuranceName(Person person) {
	if (person != null) {
    	Car car = person.getCar();
        if (car !=null) {
        	Insurance insurance = car.getInsurance();
            if (insurance !=null) {
            	return insurance.getName();
                }
            }
        }
    }
    
    returrn "Unknown";
}

- 365p 

 

 

null 때문에 발생하는 문제 

- 에러의 근원이다 

- 코드를 어지럽힌다

- 아무 의미가 없다

- 자바 철학에 위배된다

- 형식 시스템에 구멍을 만든다 

 

- 367p 

 

 

 

값이 있으면 Optional 클래스는 값을 감싼다. 반면 값이 없으면 Optional.empty 메서드로 Optional을 반환한다. Optional.empty는 Optional의 특별한 싱글턴 인스턴스를 반환하는 정적 팩토리 메서드다. 

 

- 369p 

 

 

 

기존 코드 

 

public class Person {
	private Car car; 
    
    public car getCar(){
    	return car;
    }
}


public class Car {
	private Insurance insurance;
    public Insurance getInsurance() { 
    	return insurance;
    }
}

 

public class Person {
	private Optional<Car> car; 
    
    public Optional<Car> getCar() {
    	return car;
    }
    
    
}

사람이 차를 소유했을 수도 소유하지 않았을 수도 있으므로 Optional로 정의한다. 

 

- 369 ~ 370p

 

 

Optional 만들기 3가지 방식 

 

빈 Optional 

 

Optional<Car> optCar = Optional.empty();

 

null이 아닌 값으로 Optional 만들기 

Optional<Car> optCar = Optional.of(car);

 

null 값으로 Optional 만들기 

Optional<Car> optCar = Optional.ofNullable(car);

 

- 371p 

 

 

Optional이 비어있는지 확인하고 반환하기 

String name = null;
if (insurance != null) {
	name = insuranc.getName();
}

 

 

Optional의 map 메서드 사용하기 

 

Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

 

 

 

flatMap을 이용해서 하나의 스트림으로 병합하여 평준화할 수 있듯이 optional에도 적용될 수 있다. 

 

public String getCarInsuranceName(Optional<Person> person) {
	return person.flatMap(Person::getCar)
    			 .flatMap?(Car::Insurance)
                 .map(Insurance::getName)
                 .orElse("Unknown");
    }

 

- 375p 

 

 

 

public String getCarInsuranceName(Optional<Person> person) {
	return person.filter(person -> person.getAge() >= minAge)
    			 .flatMap(Person::getCar)
    			 .flatMap?(Car::Insurance)
                 .map(Insurance::getName)
                 .orElse("Unknown");
    }

- 383p 

 

 

null 일 수 있는 값을 optional로 안전하게 변환하기 

 

Optional<Object> value = Optional.ofNullable(map.get("key"));

- 384p 

 

 

예외를 발생시키는 대신 optional을 반환하도록 할 수 있다. 

 

public static Optional<Integer> stringToInt(String s) {
	try {	
    	return Optional.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
    	return Optional.empty(); 
    }
}

 

- 385p 

 

 

 

프로퍼티에서 지속 시간을 읽는 명령형 코드 

public int readDuration(Properties props, String name) {
	String value = props.getProperty(name);
    if (value != null) {
    	try {
        	int i = Integer.parseInt(value);
            if (i>0) {
            	return i;
            }
        }
        } catch (NumberFormatException nfe) { } 
     } 
     return 0; 
}

=> 요청한 이름이 존재하는지 확인하고 문자를 숫자로 변환하기 위해 시도하고 양수이면 리턴하고 하나의 조건이라도 실패하면 0을 반환한다. 

 

이를 하나의 유연한 코드로 재구현하면 

 

public int readDuration(Properties props, String name) {
	return Optional.ofNullable(props.getProperty(name))
    				.flatMap(OptionalUtility::stringToInt)
                    .filter(i -> i > 0)
                    .orelse(0);
    }

 

- 387 ~ 388p 

 

 

반응형