๋ชฉ์ฐจ


null์ด ์ฃผ๋Š” ๋ฌธ์ œ

์•ž์„  ๊ธ€์—์„œ๋Š” ์ž๋ฐ” ์–ธ์–ด์—์„œ null์˜ ํŠน์ง•๊ณผ ํ•จ๊ป˜ ์ž˜๋ชป๋œ null ์ฐธ์กฐ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค. ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด, ๋Ÿฐํƒ€์ž„์—์„œ null์„ ์ž˜๋ชป ์ฐธ์กฐํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” NullPointerException์ด ๋ฐœ์ƒํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ณ  ์ด๋ฅผ ํ”ผํ•˜๊ณ ์ž ์ƒ๊ฒจ๋‚˜๋Š” ์ฒดํฌ ๋กœ์ง์œผ๋กœ ์ธํ•ด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

์‚ฌ์‹ค null ์ฒ˜๋ฆฌ๋ฅผ ๊ฐœ์„ ํ•˜๋ ค๋Š” ๋…ธ๋ ฅ์€ Optional ํด๋ž˜์Šค ์ด์ „์—๋„ ์žˆ์—ˆ๋‹ค. ์ž๋ฐ”7 ์˜ โ€œProject Coinโ€์—์„œ๋Š” ์•ˆ์ „ ํ˜ธ์ถœ(safe call)์„ ์œ„ํ•œ ์—˜๋น„์Šค ์—ฐ์‚ฐ์ž(elvis operator)๊ฐ€ ์ œ์•ˆ๋˜์—ˆ์œผ๋‚˜ ๊ฒฐ๊ณผ์ ์œผ๋กœ๋Š” ์Šน์ธ๋˜์ง€ ์•Š์•˜๋‹ค.

์—˜๋น„์Šค ์—ฐ์‚ฐ์ž๋Š” ๋ณดํ†ต ?:์„ ๋งํ•œ๋‹ค. ์‹œ๊ณ„๋ฐฉํ–ฅ์œผ๋กœ 90๋„ ํšŒ์ „ํ•˜๋ฉด ์—˜๋น„์Šค ํ”„๋ ˆ์Šฌ๋ฆฌ(Elvis Presley) ์˜ ๋ชจ์Šต์ด ์—ฐ์ƒ๋œ๋‹ค.

์ƒˆ๋กœ์šด ์•ˆ์ „์žฅ์น˜๋ฅผ ์ ์šฉํ•˜๋Š” ์ด์ ์ด ๋„์ž…ํ•˜๋Š” ๋น„์šฉ๋ณด๋‹ค ์ข‹์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๊ฒ ์ง€๋งŒ, ์ œ์•ˆ์ด ์Šน์ธ๋˜์—ˆ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ์ž๋ฐ”์—์„œ null์ด ์ฃผ๋Š” ๋ฌธ์ œ๋“ค์„ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ํšŒํ”ผํ–ˆ์„์ง€๋„ ๋ชจ๋ฅธ๋‹ค.

// ํ˜น์‹œ๋‚˜ ์ด๋Ÿฐ ์ฝ”๋“œ๊ฐ€ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์•˜์„๊นŒ?
public String getPhoneManufacturerName(Person person) {
    // ์ฒด์ด๋‹ ์ค‘์—์„œ null์ด ์žˆ๋‹ค๋ฉด, "Samsung"์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    return person?.getPhone()?.getManufacturer()?.getName() : "Samsung";
}

๊ทธ๋ ‡๋‹ค๋ฉด, ๋ณด๋‹ค ๋‚˜์€ null ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ํƒ„์ƒํ•œ Optional ํด๋ž˜์Šค๋Š” ๋ฌด์—‡์ผ๊นŒ?


Optional ํด๋ž˜์Šค

java.util.Optional<T> ํด๋ž˜์Šค๋Š” ํ•จ์ˆ˜ํ˜•(Functional) ์–ธ์–ด์ธ ์Šค์นผ๋ผ(Scala)์™€ ํ•˜์Šค์ผˆ(Haskell)์˜ ์˜ํ–ฅ์„ ๋ฐ›์•„์„œ ํƒ„์ƒํ–ˆ๋‹ค. ์•„๋งˆ ์Šค์นผ๋ผ์˜ Optional๊ณผ ํ•˜์Šค์ผˆ์˜ Maybe์˜ ์˜ํ–ฅ์ด ์•„๋‹๊นŒ ์‹ถ์ง€๋งŒ, ์ž๋ฐ”์˜ Optional์˜ ๋ชฉ์ ์€ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์ด โ€˜์—†์Œโ€™์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Š” ํด๋ž˜์Šค์— ๋‹ฌ๋ฆฐ ์ฃผ์„์„ ํ†ตํ•ด์„œ๋„ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, 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.

์˜ต์…”๋„์€ ์ฃผ๋กœ โ€œ๊ฒฐ๊ณผ ์—†์Œโ€์„ ๋‚˜ํƒ€๋‚ผ ํ•„์š”๊ฐ€ ๋ถ„๋ช…ํ•˜๊ณ  null์„ ์‚ฌ์šฉํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ ๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ์˜ต์…”๋„ ํƒ€์ž…์˜ ๋ณ€์ˆ˜๋Š” ๊ทธ ์ž์ฒด๊ฐ€ null์ด๋ฉด ์•ˆ ๋˜๋ฉฐ ํ•ญ์ƒ Optional ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌ์ผœ์•ผ ํ•œ๋‹ค.

Optional์€ ๋น—๋Œ€์–ด ๋งํ•˜๋ฉด ์œ ๋ฆฌ๊ฐ™์ด ํŒŒ์†๋˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์„ ํฌ์žฅํ•  ๋•Œ ๊ฐ์‹ธ๋Š” ์—์–ด์บก(์ผ๋ช… ๋ฝ๋ฝ์ด)๊ณผ ๊ฐ™๋‹ค. ์‰ฝ๊ฒŒ ๊นจ์ ธ๋ฒ„๋ฆฌ๋Š” ์œ ๋ฆฌ์ฒ˜๋Ÿผ, ์ž˜๋ชป ๋‹ค๋ฃจ๋ฉด ์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋Š” null์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

๊ทธ๋Ÿผ ์ด์ œ๋ถ€ํ„ฐ Optional์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ํ•˜๋‚˜์”ฉ ์•Œ์•„๋ณด์ž.


Optional ๊ฐ์ฒด ์„ ์–ธ

Optional ํด๋ž˜์Šค์˜ ๋ช…์„ธ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, ์ œ๋„ค๋ฆญ ํƒ€์ž…์œผ๋กœ ์ œ๊ณต๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์„ ์–ธ๊ณผ ๋™์‹œ์— ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ง€์ •ํ•œ๋‹ค. ์ฆ‰, Optional์ด ๊ฐ์‹ธ๋Š” ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค.

Optional<Person> person; // Person ํƒ€์ž…์˜ `Optional` ๋ณ€์ˆ˜
Optional<Phone> phone; // phone ํƒ€์ž…์˜ `Optional` ๋ณ€์ˆ˜


Optional ๊ฐ์ฒด ์ƒ์„ฑ

Optional ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ(static factory method)๊ฐ€ ์ œ๊ณต๋œ๋‹ค.

Optional.empty()

๋น„์–ด์žˆ๋Š” Optional ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” Optional ํด๋ž˜์Šค๊ฐ€ ๊ฐ€์ง„ ์‹ฑ๊ธ€ํ„ด ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค. โ€œ๋น„์–ด์žˆ๋‹คโ€ ๋ผ๋Š” ์˜๋ฏธ์—์„œ null๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, Optional.empty()๋Š” ์ฐธ์กฐํ•˜๋”๋ผ๋„ NullPointerException๊ณผ ๊ฐ™์€ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ์ ์ด ๋‹ค๋ฅด๋‹ค.

// `Optional` ํด๋ž˜์Šค์˜ ์ •์  ๋ฉค๋ฒ„๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์Œ
private static final Optional<?> EMPTY = new Optional<>();

public static<T> Optional<T> empty() {
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

Optional.of(T value)

null์ด ์•„๋‹Œ ๊ฐ’์„ ๊ฐ์‹ธ๋Š” Optional ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ฐ’์ด null์ธ ๊ฒฝ์šฐ NullPointerException์ด ๋ฐœ์ƒํ•œ๋‹ค.

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

Optional.ofNullable(T value)

null ๊ฐ’์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” Optional ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๊ฐ’์ด null์ธ ๊ฒฝ์šฐ ๋นˆ Optional ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ธ์ž๋กœ ๋„˜๊ฒจ์ง€๋Š” ๊ฐ’์ด null์ผ์ง€๋„ ๋ชจ๋ฅด๋Š” ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}


๊ฐ์ฒด๋Š” ์ƒ์„ฑํ–ˆ๋Š”๋ฐ, ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜์ง€?

์ด๋ฒˆ๊ธ€์—์„œ๋Š” Optional ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์†Œ๊ฐœ์™€ Optional ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค. ์ด์–ด์ง€๋Š” ๊ธ€์—์„œ๋Š” Optional ๊ฐ์ฒด์˜ ๊ฐ’์„ ํ•„ํ„ฐ๋ง ํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์†Œ๊ฐœํ•œ๋‹ค.