Post

๐Ÿฃ chap11. null ๋Œ€์‹  Optional ํด๋ž˜์Šค

๊ฐ’์ด ์—†๋Š” ์ƒํ™ฉ์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ๊นŒ?

๋‹ค์Œ ์ฝ”๋“œ์—์„œ๋Š” ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ• ๊นŒ?

1
2
3
public String getCarInsuranceName(Person person) {
	return person.getCar().getInsurance().getName();
}

์ฝ”๋“œ์—์„œ ์ž๋™์ฐจ๋ฅผ ์†Œ์œ ํ•˜์ง€ ์•Š์€ ์‚ฌ๋žŒ์ด๋ผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

์ž๋™์ฐจ๋Š” ์†Œ์œ ํ•˜๊ณ  ์žˆ์ง€๋งŒ ๋ณดํ—˜์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด?

โ†’ NullPointerException์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ

๋ณด์ˆ˜์ ์ธ ์ž์„ธ๋กœ NullPointerException ์ค„์ด๊ธฐ

์˜ˆ๊ธฐ์น˜ ์•Š์€ NullPointerException์„ ํ”ผํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?

โ†’ ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ํ•„์š”ํ•œ ๊ณณ์— ๋‹ค์–‘ํ•œ null ํ™•์ธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ null ์˜ˆ์™ธ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์—ฌ ํ•  ๊ฒƒ์ด๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
			}
		}
	}

	return "Unknown";
}

์ด์™€ ๊ฐ™์€ ๋ฐ˜๋ณต ํŒจํ„ด ์ฝ”๋“œ๋ฅผ โ€˜๊นŠ์€ ์˜์‹ฌโ€˜์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

์ด๋ฅผ ๋ฐ˜๋ณตํ•˜๋‹ค ๋ณด๋ฉด ์ฝ”๋“œ์˜ ๊ตฌ์กฐ๊ฐ€ ์—‰๋ง์ด ๋˜๊ณ  ๊ฐ€๋…์„ฑ๋„ ๋–จ์–ด์ง„๋‹ค.

โ†’ ๋ญ”๊ฐ€ ๋‹ค๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public String getCarInsuranceName(Person person) {

	if (person != null) {
		return "Unknown";
	}

	Car car = person.getCar();

	if (car != null) {
		return "Unknown";
	}

	Insurance insurance = car.getInsurance();

	if (insurance != null) {
		return "Unknown";
	}

	return insurance.getName();
}

์œ„ ์ฝ”๋“œ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ค‘์ฒฉ if ๋ธ”๋ก์„ ์—†์•ด๋‹ค.

null์ผ ๋•Œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ธฐ๋ณธ๊ฐ’ โ€˜Unknownโ€™์ด ์„ธ ๊ณณ์—์„œ ๋ฐ˜๋ณต๋˜๊ณ  ์žˆ๋Š”๋ฐ ๊ฐ™์€ ๋ฌธ์ž์—ด์„ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ์˜คํƒ€ ๋“ฑ์˜ ์‹ค์ˆ˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

null ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ

  • ์ž๋ฐ”์—์„œ null ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋ก ์ , ์‹ค์šฉ์  ๋ฌธ์ œ
  • ์—๋Ÿฌ์˜ ๊ทผ์›์ด๋‹ค - NullPointerException์€ ์ž๋ฐ”์—์„œ ๊ฐ€์žฅ ํ”ํžˆ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋‹ค
  • ์ฝ”๋“œ๋ฅผ ์–ด์ง€๋ŸฝํžŒ๋‹ค : null ํ™•์ธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๊ณผ๋„ํ•œ ์ฒดํฌ ๋กœ์ง์œผ๋กœ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.
  • ์•„๋ฌด ์˜๋ฏธ๊ฐ€ ์—†๋‹ค : ์ •์  ํ˜•์‹ ์–ธ์–ด์—์„œ ๊ฐ’์ด ์—†์Œ์„ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์ ์ ˆํ•˜์ง€ ์•Š๋‹ค.
  • ์ž๋ฐ” ์ฒ ํ•™์— ์œ„๋ฐฐ
  • ํ˜•์‹ ์‹œ์Šคํ…œ์— ๊ตฌ๋ฉ์„ ๋งŒ๋“ ๋‹ค.

Optional ํด๋ž˜์Šค ์†Œ๊ฐœ

Optional์€ ์„ ํƒํ˜•๊ฐ’์„ ์บก์Šํ™”ํ•˜๋Š” ํด๋ž˜์Šค๋‹ค.

๊ฐ’์ด ์žˆ์œผ๋ฉด Optional ํด๋ž˜์Šค๋Š” ๊ฐ’์„ ๊ฐ์‹ผ๋‹ค.

๋ฐ˜๋ฉด ๊ฐ’์ด ์—†์œผ๋ฉด Optional.empty ๋ฉ”์„œ๋“œ๋กœ Optional์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์˜๋ฏธ์ƒ์œผ๋ก  ๋‘˜์ด ๋น„์Šทํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์ฐจ์ด์ ์ด ๋งŽ๋‹ค.

null์„ ์ฐธ์กฐํ•˜๋ ค ํ•˜๋ฉด NullPointerException์ด ๋ฐœ์ƒํ•˜์ง€๋งŒ Optional.empty()๋Š” Optional ๊ฐ์ฒด์ด๋ฏ€๋กœ ์ด๋ฅผ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Optional ์ ์šฉ ํŒจํ„ด

๋นˆ OptionalOptional.empty๋กœ ๋นˆ Optional ๊ฐ์ฒด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. Optional optCar = Optional.empty();

null์ด ์•„๋‹Œ ๊ฐ’์œผ๋กœ Optional ๋งŒ๋“ค๊ธฐ Optional optCar = Optional.of(car);์ด์ œ car๊ฐ€ null์ด๋ผ๋ฉด ์ฆ‰์‹œ NullPointerException์ด ๋ฐœ์ƒํ•œ๋‹ค(Optional์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด car์˜ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•˜๋ ค ํ•  ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๊ฒƒ์ด๋‹ค. )

null๊ฐ’์œผ๋กœ Optional ๋งŒ๋“ค๊ธฐ Optional optCar = Optional.ofNullable(car);car๊ฐ€ null์ด๋ฉด ๋นˆ Optional ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ Optional์ด ๋น„์–ด์žˆ์œผ๋ฉด get์„ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ฆ‰, Optional์„ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฐ๊ตญ null์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๊ฒช์„ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ Optional๋กœ ๋ช…์‹œ์ ์ธ ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.

๋งต์œผ๋กœ Optional์˜ ๊ฐ’์„ ์ถ”์ถœํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๊ธฐ

๋ณดํ†ต ๊ฐ์ฒด์˜ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•  ๋•Œ๋Š” Optional์„ ์‚ฌ์šฉํ•  ๋•Œ๊ฐ€ ๋งŽ๋‹ค.

1
2
3
4
5
String name = null;

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

์ด๋Ÿฐ ์œ ํ˜•์˜ ํŒจํ„ด์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Optional์€ map ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.

Optional optInsurance = Optional.ofNullable(insurance);

Optional name = optInsurance.map(Insurance::getName);

Optional์ด ๊ฐ’์„ ํฌํ•จํ•˜๋ฉด map์˜ ์ธ์ˆ˜๋กœ ์ œ๊ณต๋œ ํ•จ์ˆ˜๊ฐ€ ๊ฐ’์„ ๋ฐ”๊พผ๋‹ค. optional์ด ๋น„์–ด ์žˆ์œผ๋ฉด ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

flatMap์œผ๋กœ Optional ๊ฐ์ฒด ์—ฐ๊ฒฐ

1
2
3
4
5
Optional<Person> optPerson = Optional.of(person);

Optional<String> name = optPerson.map(Person::getCar)
	.map(Car::getInsurance)
	.map(Insurance::getName);

์•ˆํƒ€๊น๊ฒŒ๋„ ์œ„ ์ฝ”๋“œ๋Š” ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋ณ€์ˆ˜ optPeople์˜ ํ˜•์‹์€ Optional์ด๋ฏ€๋กœ map ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ๋‹ค์Œ ์—ฐ์‚ฐ์€ Optional> ํ˜•์‹์˜ ๊ฐ์ฒด์ด๋ฏ€๋กœ getInsurance ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ŠคํŠธ๋ฆผ์˜ flatMap์€ ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋‹ค. ์ฆ‰ flatMap์€ ์ธ์ˆ˜๋กœ ๋ฐ›์€ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•ด์„œ ์ƒ์„ฑ๋œ ๊ฐ๊ฐ์˜ ์ŠคํŠธ๋ฆผ์—์„œ ์ฝ˜ํ…์ธ ๋งŒ ๋‚จ๊ธฐ๊ฒŒ ๋˜๋ฏ€๋กœ ์œ„์™€ ๊ฐ™์€ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
Optional<Person> optPerson = Optional.of(person);

Optional<String> name = optPerson.flatMap(Person::getCar)
	.flatMap(Car::getInsurance)
	.map(Insurance::getName)
	.orElse("Unknown");

๋””ํดํŠธ ์•ก์…˜๊ณผ Optional ์–ธ๋žฉ

Optional ํด๋ž˜์Šค๋Š” Optional ์ธ์Šคํ„ด์Šค์—์„œ ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

get() : ๋ž˜ํ•‘๋œ ๊ฐ’์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๊ฐ’์ด ์—†์œผ๋ฉด NoSuchElementException์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

orElse(T other) : Optional์ด ๊ฐ’์„ ํฌํ•จํ•˜์ง€ ์•Š์„ ๋•Œ ๋””ํดํŠธ๊ฐ’์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

orElseGet(Supplier<? extends T> other) : orElse ๋ฉ”์„œ๋“œ์— ๋Œ€์‘ํ•˜๋Š” ๊ฒŒ์œผ๋ฅธ ๋ฒ„์ „์˜ ๋ฉ”์„œ๋“œ๋‹ค. Optional์— ๊ฐ’์ด ์—†์„ ๋•Œ๋งŒ Supplier๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

orElseThrow(Supplier<? extends T> exceptionSupplier) : Optional์ด ๋น„์–ด์žˆ์„๋•Œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค๋Š” ์ ์—์„œ get ๋ฉ”์„œ๋“œ์™€ ๋น„์Šทํ•˜๋‹ค

ifPresent(Consumer<? super T> consumer) : ๊ฐ’์ด ์กด์žฌํ•  ๋•Œ ์ธ์ˆ˜๋กœ ๋„˜๊ฒจ์ค€ ๋™์ž‘์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ’์ด ์—†์œผ๋ฉด ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

๋‘ Optional ํ•ฉ์น˜๊ธฐ

์ด์ œ Person๊ณผ Car ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ€์žฅ ์ €๋ ดํ•œ ๋ณด๋Ÿผ๋ฃŒ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ณดํ—˜ํšŒ์‚ฌ๋ฅผ ์ฐพ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„

1
2
3
4
5
6
7
8
9
public Optional<Insurance> nullSafeFindCheapestInsurance(
	Optional<Person> person, Optional<Car> car) {
		if (person.isPresent() && car.isPersent()) {
			return Optional.of(findCheapestInsurance(person.get(), car.get());
		} else {
			return Optional.empty();
		}
	}
}

๊ตฌํ˜„ ์ฝ”๋“œ๋Š” null ํ™•์ธ ์ฝ”๋“œ์™€ ํฌ๊ฒŒ ๋‹ค๋ฅธ์ ์ด ์—†๋‹ค.

Optional ํด๋ž˜์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด์„œ ์ด์ฝ”๋“œ๋ฅผ ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์—†์„๊นŒ?

1
2
3
4
public Optional<Insurance> nullSafeFindCheapestInsurance(
	Optional<Person> person, Optional<Car> car) {
		return person.flatMap(p -> car.map(c ->findCheapestInsurance(p, c));
}

์ฒซ ๋ฒˆ์งธ Optional์— flatMap์„ ํ˜ธ์ถœํ–ˆ์œผ๋ฏ€๋กœ ์ฒซ ๋ฒˆ์งธ Optional์ด ๋น„์–ด์žˆ๋‹ค๋ฉด ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•œ ๋žŒ๋‹ค ํ‘œํ˜„์‹์ด ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ๋นˆ Optional์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

ํ•„ํ„ฐ๋กœ ํŠน์ •๊ฐ’ ๊ฑฐ๋ฅด๊ธฐ

์ข…์ข… ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์–ด๋–ค ํ”„๋กœํผํ‹ฐ๋ฅผ ํ™•์ธํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ณดํ—˜ํšŒ์‚ฌ ์ด๋ฆ„์ด โ€œCambridgeInsuranceโ€์ธ์ง€ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž

1
2
3
4
5
Insurance insurance = ...;

if(insurance != null && "CambridgeInsurance".equals(insurance.getName())) {
	System.out.println("ok");
}

Optional ๊ฐ์ฒด์— filter ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์žฌ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
Optional<Insurance> optInsurance = ...;

optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName())).ifPresent(x -> System.out.println("ok"));

Optional์ด ๋น„์–ด์žˆ๋‹ค๋ฉด filter ์—ฐ์‚ฐ์€ ์•„๋ฌด๋™์ž‘๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์˜ˆ์™ธ์™€ Optional

Integer.parseInt(String)์€ ์ •์  ๋ฉ”์„œ๋“œ

์ด ๋ฉ”์„œ๋“œ๋Š” ๋ฌธ์ž์—ด์„ ์ •์ˆ˜๋กœ ๋ฐ”๊พธ์ง€ ๋ชปํ•  ๋•Œ NumberFormatException์„ ๋ฐœ์ƒ

์ฆ‰, ๋ฌธ์ž์—ด์ด ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์‚ฌ์‹ค์„ ์˜ˆ์™ธ๋กœ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค.

๊ธฐ์กด์— ๊ฐ’์ด null์ผ ์ˆ˜ ์žˆ์„ ๋•Œ๋Š” if ๋ฌธ์œผ๋กœ null ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ–ˆ์ง€๋งŒ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ฉ”์„œ๋“œ์—์„œ๋Š” try/catch ๋ธ”๋ก์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด ๋‹ค๋ฅด๋‹ค.

1
2
3
4
5
6
7
public static Optional<Integer> stringToInt(String s) {
	try {
		return Optional.of(Integer.parseInt(s));
	} catch (NumberFormatException e) {
		return Optional.empty();
	}
}

๊ธฐ์กด ์ž๋ฐ” ๋ฉ”์„œ๋“œ parseInt๋ฅผ ์ง์ ‘ ๊ณ ์น  ์ˆ˜๋Š” ์—†์ง€๋งŒ ๋‹ค๋ฆ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ parseInt๋ฅผ ๊ฐ์‹ธ๋Š” ์ž‘์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์„œ Optional์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

This post is licensed under CC BY 4.0 by the author.