Post

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

chap11. null ๋Œ€์‹  Optioanl ํด๋ž˜์Šค

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

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

  • ๊นŠ์€ ์˜์‹ฌ
1
2
3
4
5
6
7
8
9
10
11
12
public String getCarInsuranceName(Person person) {
		if (person != null) {
				Car car = person.getCar();
				if (car != null) {
						Insurance insurance = car.getInsurancee();
						if (insurance != null) {
								return insurance.getName();
						}
				}
		}
		return "Unknown";
}

null ํ™•์ธ ์ฝ”๋“œ ๋•Œ๋ฌธ์— ๋‚˜๋จธ์ง€ ํ˜ธ์ถœ ์ฒด์ธ์˜ ๋“ค์—ฌ์“ฐ๊ธฐ ์ˆ˜์ค€์ด ์ฆ๊ฐ€ํ•œ๋‹ค.

  • ๋„ˆ๋ฌด ๋งŽ์€ ์ถœ๊ตฌ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}

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

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

  • ์—๋Ÿฌ์˜ ๊ทผ์›
  • ์ฝ”๋“œ๋ฅผ ์–ด์ง€๋ŸฝํžŒ๋‹ค
  • ์•„๋ฌด ์˜๋ฏธ๊ฐ€ ์—†๋‹ค
  • ์ž๋ฐ” ์ฒ ํ•™์— ์œ„๋ฐฐ๋œ๋‹ค
  • ํ˜•์‹ ์‹œ์Šคํ…œ์— ๊ตฌ๋ฉ์„ ๋งŒ๋“ ๋‹ค

11.1.3 ๋‹ค๋ฅธ ์–ธ์–ด๋Š” null ๋Œ€์‹ ์— ๋ฌด์–ผ ์‚ฌ์šฉํ•˜๋‚˜?

  • ์•ˆ์ „ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์—ฐ์‚ฐ์ž (?.)
1
def carInsuranceName = parson?.car?.insurance?.name
  • Maybe
  • Option[T]

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

์ž๋ฐ” 8์€ ํ•˜์Šค์ผˆ๊ณผ ์Šค์นผ๋ผ์˜ ์˜ํ–ฅ์„ ๋ฐ›์•„ java.util.Optional ๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

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

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

๋˜ํ•œ null ๋Œ€์‹  Optional ์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ํ˜•์‹์ด Optional ๋กœ ๋ฐ”๋€Œ๋Š”๋ฐ

์ด๋Š” ๊ฐ’์ด ์—†์„ ์ˆ˜ ์žˆ์Œ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค

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

11.3.1 Optional ๊ฐ์ฒด ๋งŒ๋“ค๊ธฐ

  • ๋นˆ Optional
1
Optional<Car> optCar = Optional.empty();
  • null์ด ์•„๋‹Œ ๊ฐ’์œผ๋กœ Optional ๋งŒ๋“ค๊ธฐ
1
Optional<Car> optCar = Optional.of(car);
  • null๊ฐ’์œผ๋กœ Optional ๋งŒ๋“ค๊ธฐ
1
Optional<Car> optCar = Optional.ofNullable(car);

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

Optional ์˜ map ๋ฉ”์„œ๋“œ๋Š” ์ŠคํŠธ๋ฆผ์˜ map ๋ฉ”์„œ๋“œ์™€ ๋น„์Šทํ•˜๋‹ค

Optional ๊ฐ์ฒด๋ฅผ ์ตœ๋Œ€ ์š”์†Œ์˜ ๊ฐœ์ˆ˜๊ฐ€ ํ•œ ๊ฐœ ์ดํ•˜์ธ ๋ฐ์ดํ„ฐ ์ปฌ๋ ‰์…˜์œผ๋กœ ์ƒ๊ฐํ•˜๊ณ 

๊ฐ’์„ ํฌํ•จํ•˜๋ฉด map ์˜ ์ธ์ˆ˜๋กœ ์ œ๊ณต๋œ ํ•จ์ˆ˜๊ฐ€ ๊ฐ’์„ ๋ฐ”๊พผ๋‹ค

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

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

์ด์ฐจ์› Optional ์„ flatMap ์œผ๋กœ ์ผ์ฐจ์› Optional ๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค

  • Optional ๋กœ ์ž๋™์ฐจ์˜ ๋ณดํ—˜ํšŒ์‚ฌ ์ด๋ฆ„ ์ฐพ๊ธฐ
1
2
3
4
5
6
public String getCarInsuranceName(Optional<Person> person) {
	return person.flatMap(Person::getCar)
							.flatMap(Car::Insurance)
							.map(Insurance::getName)
							.orElse("Unknown");
}

null ์„ ํ™•์ธํ•˜๋Š๋ผ ์กฐ๊ฑด ๋ถ„๊ธฐ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ์•Š์œผ๋ฉด์„œ๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ–ˆ๋‹ค.

๋˜ ๋„๋ฉ”์ธ ๋ชจ๋ธ๊ณผ ๊ด€๋ จํ•œ ์•”๋ฌต์ ์ธ ์ง€์‹์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ํ˜•์‹ ์‹œ์Šคํ…œ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

11.3.4 Optional ์ŠคํŠธ๋ฆผ ์กฐ์ž‘

์ž๋ฐ” 9์—์„œ๋Š” Optional ์— stream() ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

1
2
3
4
5
6
7
8
public Set<String> getCarInsuranceNames(List<Person> persons) {
		return persons.stream()
									.map(Person::getCar)
									.map(optCar -> optCar.flatMap(Car::getInsurance))
									.map(optIns -> optIns.map(Insurance::getName))
									.flatMap(Optional::stream)
									.collect(toSet());
}

Optional ๋•๋ถ„์— ์—ฌ๋Ÿฌ ์—ฐ์‚ฐ์„ null ๊ฑฑ์ • ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋งˆ์ง€๋ง‰ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ๋นˆ Optional ์„ ์ œ๊ฑฐํ•˜๊ณ  ๊ฐ’์„ ์–ธ๋žฉํ•ด์•ผ ํ•œ๋‹ค.

1
2
3
4
Stream<Optional<String>> stream =
Set<String> result = stream.filter(Optional::isPresent)
													 .map(Optional::get)
													 .collect(toSet());

filter ์™€ map ์„ ์ด์šฉํ•ด 0๊ฐœ ์ด์ƒ์˜ ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š” ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๊ฐ’์„ ํฌํ•จํ•˜๋Š” Optional ์„ ์–ธ๋žฉํ•˜๊ณ  ๋น„์–ด์žˆ๋Š” Optional ์€ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ๋‹ค

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

  • get() : ๋ž˜ํ•‘๋œ ๊ฐ’์ด ์—†์œผ๋ฉด NoSuchElementException ์„ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์žฅ ์•ˆ์ „ํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.
  • orElse: ๊ฐ’์„ ํฌํ•จํ•˜์ง€ ์•Š์„ ๋•Œ ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.
  • orElseGet(Supplier<? extends T> other) : Optional ์— ๊ฐ’์ด ์—†์„ ๋•Œ๋งŒ Supplier ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.
  • orElseThrow(Supplier<? extends X> exceptionSupplier) : Optional ์ด ๋น„์–ด์žˆ์„ ๋•Œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.
  • ifPresent(Consumer<? super T> consumer : ๊ฐ’์ด ์กด์žฌํ•  ๋•Œ ์ธ์ˆ˜๋กœ ๋„˜๊ฒจ์ค€ ๋™์ž‘์„ ์‹คํ–‰ํ•œ๋‹ค

์ž๋ฐ” 9์—์„œ๋Š” ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

  • ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) : Optional ์ด ๋น„์–ด์žˆ์„ ๋•Œ Runnable์„ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ ์‹คํ–‰ํ•œ๋‹ค.

11.3.6 ๋‘ 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));
}

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

1
2
3
4
Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance ->
										"CambridgeInsurance".equals(insurance.getName())
						.ifPresent(x -> System.out.println("ok"));

11.4 Optional ์„ ์‚ฌ์šฉํ•œ ์‹ค์šฉ ์˜ˆ์ œ

11.4.1 ์ž ์žฌ์ ์œผ๋กœ null์ด ๋  ์ˆ˜ ์žˆ๋Š” ๋Œ€์ƒ์„ Optional๋กœ ๊ฐ์‹ธ๊ธฐ

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

11.4.2 ์˜ˆ์™ธ์™€ Optional ํด๋ž˜์Šค

Integer.parseInt(String) ์€ ๋ฌธ์ž์—ด์„ ์ •์ˆ˜๋กœ ๋ฐ”๊พธ์ง€ ๋ชปํ•  ๋•Œ NumberFormatException ์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

์ด๋ฅผ ๋นˆ Optional ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

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();
		}
}

11.4.3 ๊ธฐ๋ณธํ˜• Optional์„ ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•˜๋Š” ์ด์œ 

OptionalInt, OptionalLong, OptionalDouble ๋“ฑ์˜ ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

Stream ๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ Optional ์˜ ์ตœ๋Œ€ ์š”์†Œ๋Š” 1๊ฐœ์ด๋ฏ€๋กœ ๊ธฐ๋ณธํ˜• ํŠนํ™” ํด๋ž˜์Šค๋กœ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜๋Š” ์—†๋‹ค

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