[Java] ์ฌ๋ฐ๋ฅธ Optional ์ฌ์ฉ์ผ๋ก API ์ค๊ณํ๊ธฐ
API๋ฅผ ์ค๊ณํ ๋, ์ฌ์ฉ์๊ฐ ๋ฉ์๋ ์๊ทธ๋์ฒ๋ง ๋ณด๊ณ ๋ ๋ฐํ๊ฐ์ด ์ ํํ์ธ์ง, ์ฆ ๊ฐ์ด ์กด์ฌํ์ง ์์ ์ ์์์ ์ฝ๊ฒ ์ธ์งํ ์ ์๋ค๋ฉด ํจ์ฌ ์์ ํ๊ณ ๋ช ๋ฃํ API๋ฅผ ์ ๊ณตํ ์ ์์ ๊ฒ์ด๋ค. ์ด ๊ธ์์๋ Java 8๋ถํฐ ๋์ ๋ Optional ํด๋์ค๋ฅผ ํ์ฉํด API ์ค๊ณ์ ๋ช ํ์ฑ์ ๋์ด๊ณ , NullPointerException๊ณผ ๊ฐ์ ์ค๋ฅ๋ฅผ ์๋ฐฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๋ ค ํ๋ค.
1. Optional์ ๊ธฐ๋ณธ ๊ฐ๋
public final class Optional<T>
extends Object
A container object which may or may not contain a non-null value. - ๊ณต์ ๋ฌธ์ ์ ์
Java 8 ๋ถํฐ ๋์ ๋ Optional<T>๋ null์ด ๋ ์ ์๋ ๊ฐ์ ๊ฐ์ธ๋ ๋ํผ(wrapper) ํด๋์ค๋ค. ๊ฐ์ด ์กด์ฌํ ์๋, ์กด์ฌํ์ง ์์ ์๋ ์๋ ์ปจํ ์ด๋ ๊ฐ์ฒด๋ก์, null ์ฐธ์กฐ ๋์ ์ฌ์ฉํจ์ผ๋ก์จ NPE(NullPointerException)๋ฅผ ๋ฐฉ์งํ๊ณ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์ฌ์ค๋ค.
- ๊ฐ์ด ์กด์ฌํ๋ ๊ฒฝ์ฐ: ์ค์ ๊ฐ์ฒด๋ฅผ Optional๋ก ๊ฐ์ธ ๋ฐํ
- ๊ฐ์ด ์์ ๊ฒฝ์ฐ: Optional.empty()์ ๊ฐ์ด ๋น์ด์๋ Optional ์ธ์คํด์ค๋ฅผ ๋ฐํ
์๋ฅผ ๋ค์ด, Optional<Car>์ ๊ฒฝ์ฐ ์ฐจ ๊ฐ์ฒด๊ฐ ์กด์ฌํ๋ฉด ๊ทธ ๊ฐ์ ๊ฐ์ธ ๋ฐํํ๋ฉฐ, ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ ๋น Optional์ ๋ฐํํ์ฌ null ๋์ ์์ ํ ๋ฐฉ์์ผ๋ก “๊ฐ ์์”์ ํํํ๋ค.
์ด๋ฌํ ์ ๊ทผ ๋ฐฉ์์ API ์ฌ์ฉ์์๊ฒ ๋ฉ์๋ ํธ์ถ ์ null ์ฒดํฌ์ ํ์์ฑ์ ์ปดํ์ผ ํ์์ ์ธ์งํ๋๋ก ๋์์ค ์ ์๋ค.
2. API ์ค๊ณ์ Optional ์ ์ฉํ๊ธฐ
์ ์ค๊ณ๋ API๋ ๋ฉ์๋ ์๊ทธ๋์ฒ๋ง ๋ณด๊ณ ๋ ํด๋น ๋ฉ์๋๊ฐ ๋ฐํํ๋ ๊ฐ์ด ๋ฐ๋์ ์กด์ฌํ๋์ง, ์๋๋ฉด ์ ํํ์ธ์ง ๋ช
ํํ ์ ์ ์์ด์ผ ํ๋ค.
์๋ ๋ ๊ฐ์ง ์ ์ธ์ ๋น๊ตํด๋ณด์.
- ํ์ ๊ฐ ๋ฐํ
- public String getName();
- ์ด ๊ฒฝ์ฐ, ๊ฐ์ด ๋ฐ๋์ ์กด์ฌํด์ผ ํ๋ฉฐ, ๋ง์ฝ ๋ด๋ถ์ ์ผ๋ก null์ด ๋ฐ์ํ๋ค๋ฉด NullPointerException์ผ๋ก ์ด์ด์ง ์ ์๋ค.
- ์ ํํ ๊ฐ ๋ฐํ
- public Optional<String> getName();
- Optional์ ๋ฐํํจ์ผ๋ก์จ, API ์ฌ์ฉ์๋ ๋ฐํ ๊ฐ์ด ์์ ๊ฐ๋ฅ์ฑ๋ ํจ๊ป ๊ณ ๋ คํ ์ ์๊ฒ ๋๋ค. ์ด๋ฅผ ํตํด ๋ถํ์ํ null ์ฒดํฌ๋ฅผ ์ค์ด๊ณ , ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์คํ์ผ์ ๋ค์ํ ๋ฉ์๋(map, filter, ifPresent ๋ฑ)๋ฅผ ํ์ฉํ ์ ์๋ค.
3. Optional์ ์์ฑ ๋ฐ ์ฌ์ฉ
Optional ํด๋์ค๋ ์ธ ๊ฐ์ง ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ก ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.
- Optional.empty(): ๋น Optional ์ธ์คํด์ค๋ฅผ ๋ฐํ
- Optional<Car> emptyCar = Optional.empty();
- Optional.of: ๋ฐ๋์ null์ด ์๋ ๊ฐ์ ์ ๋ฌํด์ผ ํ๋ฉฐ, ๋ง์ฝ null์ด ์ ๋ฌ๋๋ฉด ์ฆ์ NullPointerException์ด ๋ฐ์ํ๋ค.
- Optional<Car> optCar = Optional.of(car); //car๊ฐ null์ผ ๊ฒฝ์ฐ NullPointerException ๋ฐ์!
- ์ด๋ ์ด๊ธฐ ๋จ๊ณ์์ ์ค๋ฅ๋ฅผ ๋ ธ์ถ์์ผ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ํด๊ฒฐํ๋๋ก ๋์์ค๋ค.
- Optional.ofNullable: ๊ฐ์ด null์ผ ๊ฐ๋ฅ์ฑ์ด ์์ ๋ ์ฌ์ฉํ๋ฉฐ, null์ด๋ฉด ๋น Optional ๊ฐ์ฒด(Optional.empty())๋ฅผ ๋ฐํํ๋ค.
- Optional<Car> optCar = Optional.ofNullable(car);
- ์ด ๋ฐฉ์์ ํตํด null ์ฒดํฌ๋ฅผ ๋ณด๋ค ๋ช ์์ ์ด๊ณ ์์ ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
- ์ถ๊ฐ์ ์ผ๋ก, ๊ธฐ์กด์ null ์ฒดํฌ ํจํด์ ๋์ฒดํ๊ธฐ ์ํด Optional์ map ๋ฉ์๋๋ฅผ ํ์ฉํ ์ ์๋ค.
- Optional<Insurance> optInsurance = Optional.ofNullable(insurance); // null์ผ ์ ์์(null ์ฒดํฌ ํ์)
Optional<String> name = optInsurance.map(Insurance::getName); - ์ด ๋ฐฉ์์ insurance๊ฐ null์ด ์๋ ๊ฒฝ์ฐ์๋ง getName()์ ํธ์ถํ๊ณ , null์ธ ๊ฒฝ์ฐ ๋น Optional์ ๊ทธ๋๋ก ๋ฐํํ๋ค.
- Optional<Insurance> optInsurance = Optional.ofNullable(insurance); // null์ผ ์ ์์(null ์ฒดํฌ ํ์)
4. Optional์ ํ์ฉํ API ์ค๊ณ์ ์ฅ์
4.1. ๋ช ์์ ์๋ ํํ
๋ฉ์๋ ์๊ทธ๋์ฒ์ Optional์ ์ฌ์ฉํ๋ฉด, ํด๋น ๊ฐ์ด ์ ํ์ ์์ ๋ช ์์ ์ผ๋ก ํํํ ์ ์๋ค.
- API ์ฌ์ฉ์ ์ธ์: ๋ฐํ ํ์ ์ ํตํด ์ฌ์ฉ์๋ ๊ฐ์ด ์์ ์ ์์์ ์ธ์งํ๊ณ , ์ ์ ํ ์์ธ ์ฒ๋ฆฌ๋ ๋์ฒด ๋ก์ง์ ๊ตฌํํ ์ ์๊ฒ ๋๋ค.
4.2. ์ฝ๋ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ ํฅ์
Optional์ null ์ฒดํฌ๋ฅผ ์ํ ๋ถํ์ํ if ๋ฌธ์ ์ค์ฌ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์ฌ์ค๋ค.
- ํจ์ํ ์คํ์ผ: map, flatMap, filter ๋ฑ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋์ฑ ์ ์ธ์ ์ธ ์ฝ๋ ์์ฑ์ด ๊ฐ๋ฅํด์ง๋ค.
- ์๋ฌ ๋ฐ์ ์์ : Optional.of๋ฅผ ์ฌ์ฉํ๋ฉด null ์ ๋ฌ ์์ ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ฌ, ๋ฌธ์ ์ ์์ธ์ ๋น ๋ฅด๊ฒ ํ์ ํ ์ ์๋ค.
5. Optional ์ฌ์ฉ ์ ๊ณ ๋ คํด์ผ ํ ์ฌํญ
5.1. ๊ณผ๋ํ ๋จ์ฉ ๋ฐฉ์ง
Optional์ ๋ฐํ ํ์ ์์์ ์ฌ์ฉ์ด ๊ถ์ฅ๋๋ฉฐ, ํ๋๋ ์ปฌ๋ ์ ์์๋ก ์ฌ์ฉํ๋ ๊ฒ์ ํผํ๋ ๊ฒ์ด ์ข๋ค. (ํ๋๋ก ์ฌ์ฉํ์๋ ๋ฐ์ ๊ฐ๋ฅํ ๋ฌธ์ ์ ์ ์๋์ ์ถ๊ฐ๋ก ์ ๋ฆฌํ์๋ค.)
- ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋: Optional ๊ฐ์ฒด๋ ๋ํผ(wrapper) ํด๋์ค์ด๋ฏ๋ก ๋ถํ์ํ๊ฒ ๋จ์ฉํ ๊ฒฝ์ฐ ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ ์ ์๋ค.
5.2. get() ๋ฉ์๋ ์ฌ์ฉ ์ฃผ์
Optional์ get() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด, ๊ฐ์ด ์์ ๊ฒฝ์ฐ NoSuchElementException์ด ๋ฐ์ํ๋ค.
- ๋์: orElse, orElseThrow, ifPresent ๋ฑ์ ๋ฉ์๋๋ฅผ ํ์ฉํด ๋ณด๋ค ์์ ํ๊ฒ ๊ฐ์ ๋ค๋ฃฐ ์ ์๋ค.
5.2. Value-Based Class ํน์ฑ ์ ์
Value-Based Class๋ ๋ค์๊ณผ ๊ฐ์ ํน์ฑ์ ๊ฐ๋๋ค.
- ์๋ณ์ฑ ์์(No identity): ์ธ์คํด์ค๊ฐ ๊ณ ์ ํ ์๋ณ์ฑ์ ๊ฐ์ง ์์
- ๋ถ๋ณ์ฑ(Immutability): ์์ฑ ํ ์ํ๊ฐ ๋ณ๊ฒฝ๋์ง ์์
- ๊ฐ ์ค์ฌ(Value-oriented): ์ฐธ์กฐ๊ฐ ์๋ ๊ฐ์ผ๋ก ์ทจ๊ธ๋จ
Optional์ value-based class์ด๊ธฐ ๋๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ ์์ ์ ํผํด์ผ ํ๋ค.
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided. - ๊ณต์ ๋ฌธ์ ์ฐธ๊ณ
- ๋์ผ์ฑ ๊ฒ์ฌ (==) ์ฌ์ฉ ๊ธ์ง (identity-sensitive ์ฐ์ฐ ์ ์์ธก ๋ถ๊ฐ๋ฅํ ๊ฒฐ๊ณผ๊ฐ ๋ฐ์ํ ์ ์๋ค.)
- ์ฆ, ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๊ฐ ์๋ ๋ด๋ถ ์ํ(๊ฐ)์ ๊ธฐ๋ฐ์ผ๋ก ์ฐ์ฐํ๋ค๋ ์ ์ ์ ์ํด์ผ ํ๋ค.
- Optional์ equals()๋ ๋ด๋ถ ๊ฐ์ equals()๋ฅผ ๋น๊ตํ์ฌ ๊ธฐ๋ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๋ค. (equals()/hashCode() ์ ์ ํ์)
Optional<String> opt1 = Optional.of("value");
Optional<String> opt2 = Optional.of("value");
// ๋ฌธ์ ๊ฐ ๋ ์ ์๋ ์ฝ๋
boolean result = (opt1 == opt2); // ์์ธก ๋ถ๊ฐ๋ฅํ ๊ฒฐ๊ณผ
// ๋์ .equals() ์ฌ์ฉ
boolean safeResult = opt1.equals(opt2); // ์ฌ๋ฐ๋ฅธ ๋น๊ต ๋ฐฉ๋ฒ
- JVM์ value-based class์ ์ต์ ํ๋ฅผ ์ํด ๊ฐ์ ๊ฐ์ ๊ฐ์ง ์ธ์คํด์ค๋ฅผ ๋ด๋ถ์ ์ผ๋ก ์บ์ํ์ฌ ๋์ผํ ์ฐธ์กฐ๋ก ์ฒ๋ฆฌํ ์ ์๋ค. ํ์ง๋ง ์ด๋ ๋ณด์ฅ๋ ๋์์ด ์๋๋ฏ๋ก == ์ฐ์ฐ์ ์ฌ์ฉ์ ์ง์ํด์ผ ํ๋ค. (JVM์ value-based class์ ๋ํด ์ธ์คํด์ค ํ๋ง, ์ธ๋ผ์ด๋, ์ค์นผ๋ผ ๋์ฒด ๋ฑ ๋ค์ํ ์ต์ ํ๋ฅผ ์ํํ ์ ์๋๋ฐ JVM ๊ตฌํ/๋ฒ์ ์ ๋ฐ๋ผ ์๋ณ์ฑ ๊ธฐ๋ฐ ์ฐ์ฐ์ ๊ฒฐ๊ณผ๊ฐ ์์ธก ๋ถ๊ฐ๋ฅํด์ง ์ ์๋ค๊ณ ํ๋ค.)
- identityHashCode()๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ Optional ์ธ์คํด์ค๋ฅผ ๋๊ธฐํ์ ๋์์ผ๋ก ์ผ๋ ๊ฒ ์ญ์ ๋ด๋ถ ๊ตฌํ์ ๋ฐ๋ผ ์์ธกํ ์ ์๋ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ ์ ์์ผ๋ฏ๋ก ํผํด์ผ ํ๋ค.
- ์ฆ, Optional ๋น๊ต์ equals()๋ hashCode() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ผ ํ๋ฉฐ, ๋๊ธฐํ ๋ธ๋ก์ ๋ชจ๋ํฐ ๊ฐ์ฒด๋ก ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
- Optional ์ธ์คํด์ค ์์ฒด๋ ์๋ก ์์ฑ๋์ง๋ง, ๋ด๋ถ ๊ฐ์ ์ฐธ์กฐ๋ ์๋ณธ ๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ค. (์๋ ์์ ์ฐธ๊ณ )
StringBuilder sb1 = new StringBuilder("A");
StringBuilder sb2 = sb1; // ๋์ผ ๊ฐ์ฒด ์ฐธ์กฐ
Optional<StringBuilder> opt1 = Optional.of(sb1);
Optional<StringBuilder> opt2 = Optional.of(sb2);
System.out.println(opt1.get() == opt2.get()); // true
sb1.append("B"); // ๊ฐ๋ณ ๊ฐ์ฒด ์์
System.out.println(opt1.get()); // "AB" ์ถ๋ ฅ (๋์ผ ๊ฐ์ฒด ์ํ ๋ณ๊ฒฝ)
System.out.println(opt2.get()); // "AB"
System.out.println(opt1.get() == opt2.get()); //true
5.3. API ๋ฌธ์ํ
Optional์ ์ฌ์ฉํ ๋ฉ์๋์ ๊ฒฝ์ฐ, API ๋ฌธ์์ ๊ฐ์ด ์๋ ์ํฉ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ๋ช ํํ๊ฒ ๊ธฐ์ ํด ๋๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
์ ๋ฆฌ
Optional ํด๋์ค์ ์ฌ์ฉ์ API ์ค๊ณ์ ์์ด null ์์ ์ฑ๊ณผ ๋ช ์์ ์๋๋ฅผ ํํํ๋ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์ด๋ค.
- ๋ช ํ์ฑ ์ ๊ณต: ๋ฉ์๋ ์๊ทธ๋์ฒ๋ง์ผ๋ก๋ ๋ฐํ๊ฐ์ ์ ํ์ ํน์ฑ์ ํ์ ํ ์ ์๊ฒ ํ์ฌ, API ์ฌ์ฉ์๊ฐ ๋ฐ์ ๊ฐ๋ฅํ ๋ฌธ์ ๋ฅผ ๋ฏธ๋ฆฌ ์ธ์งํ๊ณ ๋์ํ ์ ์๋๋ก ํ๋ค.
- ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์ง์: ๋ค์ํ ํจ์ํ ์ฐ์ฐ์ ํ์ฉํจ์ผ๋ก์จ ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ด๋ฉฐ, ๊ฐ๋ฐ์๊ฐ ๋ณด๋ค ์ ์ธ์ ์ธ ๋ฐฉ์์ผ๋ก ๋ก์ง์ ๊ตฌํํ ์ ์๋ค.
6. Optional์ ์ฌ์ฉ์ ์ง์ํด์ผ ๋๋ ๊ฒฝ์ฐ
6.1. ํด๋์ค ํ๋๋ก ์ฌ์ฉ ์ง์
Optional์ Serializable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ง ์๊ธฐ ๋๋ฌธ์ ์ง๋ ฌํ๊ฐ ํ์ํ ํด๋์ค์ ํ๋๋ก ์ฌ์ฉํ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
(Serializable์ ๊ตฌํํ์ง ์์์ผ๋ฉฐ final ํด๋์ค์ด๊ธฐ ๋๋ฌธ์ ์๋ธํด๋์ฑ์ ํตํ ์ง๋ ฌํ ๊ตฌํ๋ ๋ถ๊ฐ)
๋๋ฉ์ธ ๋ชจ๋ธ ์ค๊ณ์ Optional์ ์ฌ์ฉํ์ง ๋ง๋ผ๋ ์๊ฒฌ์ด ๋ง์์ง๋ง,
"๋ชจ๋ ์๋ฐ ์ธ ์ก์ "์ ์ ์๋ ์ง๋ ฌํ๊ฐ ํ์ํ ํ๋์์๋ Optional ๋์ ์์ ํ์ ๋๋ nullable ํ์ ์ ์ฌ์ฉํ๊ณ , Optional๋ก ๊ฐ์ ๋ฐํ๋ฐ์ ์ ์๋ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ์์ผ๋ก๋ผ๋ Optional์ ๋๋ฉ์ธ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ธ์ ์ ์ผ๋ก ์๊ฐํ๋ ๊ฒ ๊ฐ๋ค. (์๋ ์ฐธ๊ณ )
// ์ํฐ ํจํด: ํ๋๋ก Optional ์ฌ์ฉ
public class User {
private String name;
private Optional<Address> address; // ์ง๋ ฌํ ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ
}
// ์ฌ๋ฐ๋ฅธ ์ค๊ณ
public class User {
private String name;
private Address address; // address๋ null์ผ ์ ์์
public Optional<Address> getAddress() {
return Optional.ofNullable(address);
}
}
๊ธฐ๋ณธ Jackson ์ค์ ์ผ๋ก Optional ํ๋๋ฅผ ์ง๋ ฌํํ๋ฉด java 8์์๋ {"present":true}, java 15์์๋ {"empty":false,"present":true}๊ณผ ๊ฐ์ด ์์๊ณผ ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ ๋์ค๊ณ , ์ญ์ง๋ ฌํ ์์๋ MismatchedInputException, InvalidDefinitionException ๋ฑ์ ์์ธ๊ฐ ๋ฐ์ํ๋ค๊ณ ํ๋ค.
Optional์ ํ๋๋ก ์ฌ์ฉํ๋ฉฐ ์ด๋ฌํ ์ง๋ ฌํ ๋ฌธ์ ๋ Jackson-datatype-jdk8 ์์กด์ฑ ์ถ๊ฐ๋ฅผ ํตํด Optional ๊ฐ์ ์ผ๋ฐ ๊ฐ์ฒด๋ก ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค๊ณ ํ๋ค.
Jackson์ด ๋น์ด ์๋ ๊ฐ์ฒด๋ฅผ null๋ก, ๊ฐ์ด ์๋ ๊ฐ์ฒด๋ ํด๋น ๊ฐ์ ํฌํจํ๋ ํ๋๋ก ์ฒ๋ฆฌํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. (jackson-modules-java8 ํ๋ก์ ํธ ๋งํฌ ์ฐธ๊ณ ) https://github.com/FasterXML/jackson-modules-java8
๊ทธ๋ฌ๋ JSON์์ null์ Optional.empty()๋ก ๋ณํํ์ง ์๊ณ Optional ์์ฒด๋ฅผ null๋ก ์ด๊ธฐํํ๊ธฐ ๋๋ฌธ์ ์ฌ์ ํ null ์ฒ๋ฆฌ ๋ ผ๋ฆฌ ์ถฉ๋ํ๋ ๋ฌธ์ ๊ฐ ์กด์ฌํ๋ค.
๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ ์๋๋ฆฌ์ค
{"something": null} → something ํ๋๊ฐ null (Optional.empty() ์๋)
// ์์: Optional.empty()
// ์ค์ : Test.something = null → NPE ๋ฐ์ ๊ฐ๋ฅ
์กฐ์ฌ ๊ฒฐ๊ณผ Jdk8Module + NON_ABSENT + ๊ธฐ๋ณธ๊ฐ ์ด๊ธฐํ์ ๋ฐฉ๋ฒ์ผ๋ก ์ผ๋ฐ์ ์ธ null ๋ฐฉ์ง๊ฐ ๊ฐ๋ฅํ์ง๋ง JSON์์ ๋ช
์์ ์ผ๋ก null ์ ์ก ์ ์ปค์คํ
์ญ์ง๋ ฌํ๊ฐ ํ์ํด ๋ณด์ธ๋ค.
์ด๋ฌํ ์ฌ๋ฌ ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ ์์งํ๋ค๋ฉด ๋๋ฉ์ธ ์ค๊ณ์์๋ ํ์ํ ๊ฒฝ์ฐ ์ ํ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ์ง๋ง, ๊ฐ์ธ์ ์ผ๋ก๋ ์์ง ์์ง ๋ชปํ๋ ์ฌ๋ฌ ๋ฌธ์ ๊ฐ ๋ด์ ๋์ด ์๋ ๊ฒ ๊ฐ์ ์์ Optional์ ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข๋ค๊ณ ์๊ฐํ๋ค.
6.2. ์์ฑ์๋ ๋ฉ์๋์ ๋งค๊ฐ๋ณ์๋ก ์ฌ์ฉ ์ง์
Optional์ ๋งค๊ฐ๋ณ์๋ก ์ฌ์ฉํ๋ฉด ์ฝ๋๊ฐ ๋ถํ์ํ๊ฒ ๋ณต์กํด์ง ์ ์๋ค.
// ์ํฐ ํจํด: ๋งค๊ฐ๋ณ์๋ก Optional ์ฌ์ฉ
User user = new User("john@gmail.com", "1234", Optional.empty());
// ๊ถ์ฅ
User user = new User("john@gmail.com", "1234", null);
// ๋ค๋ฅธ ๋์: ๋น๋ ํจํด์ด๋ ์ค๋ฒ๋ก๋๋ ์์ฑ์ ์ฌ์ฉ
User user = new User.Builder()
.email("john@gmail.com")
.password("1234")
.build();
6.3. ์ปฌ๋ ์ ์ ๋ํ Optional ์ฌ์ฉ ์ง์
๋น ์ปฌ๋ ์ ์ ๋ฐํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ผ๋ก ๊ถ์ฅ๋๋ค.
// ์ํฐ ํจํด
public Optional<List<User>> getUsers() { ... }
// ๋น ๋ฆฌ์คํธ ๋ฐํ
public List<User> getUsers() {
return users != null ? users : Collections.emptyList();
}
7. orElse vs orElseGet ์ ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ
orElse๋ ํญ์ ํ๊ฐ๋์ง๋ง, orElseGet์ Optional์ด ๋น์ด์์ ๋๋ง ํ๊ฐ๋๋ค.
// ํญ์ createDefaultUser()๊ฐ ํธ์ถ๋จ
User user = userOpt.orElse(createDefaultUser());
// Optional์ด ๋น์ด์์ ๋๋ง createDefaultUser()๊ฐ ํธ์ถ๋จ
User user = userOpt.orElseGet(() -> createDefaultUser());
7.1. orElse ์ฌ์ฉ ์๋๋ฆฌ์ค- ์ฆ์ ํ๊ฐ
User user = userOpt.orElse(createDefaultUser());
- step 1: createDefaultUser()๊ฐ ๋จผ์ ํธ์ถ๋์ด ๊ธฐ๋ณธ ์ฌ์ฉ์ ๊ฐ์ฒด๋ฅผ ์์ฑ
- step 2: ์์ฑ๋ ๊ธฐ๋ณธ ์ฌ์ฉ์ ๊ฐ์ฒด๊ฐ ์ธ์๋ก ์ ๋ฌ
- step 3: userOpt๊ฐ ๊ฐ์ ๊ฐ์ง๊ณ ์๋ค๋ฉด, ๊ทธ ๊ฐ์ด ๋ฐํ๋์ง๋ง ์ด๋ฏธ createDefaultUser()๊ฐ ํธ์ถ๋์ด ๋น์ฉ์ด ๋ฐ์
- step 4: ๋ง์ฝ userOpt๊ฐ ๋น์ด์๋ค๋ฉด, ์ธ์๋ก ์ ๋ฌ๋ ๊ธฐ๋ณธ ์ฌ์ฉ์ ๊ฐ์ฒด๊ฐ ๋ฐํ
์ฆ, ํญ์ createDefaultUser()๊ฐ ์คํ๋๋ฏ๋ก, ์ค์ ๋ก ๊ธฐ๋ณธ๊ฐ์ด ํ์ ์๋๋ผ๋ ๋ถํ์ํ ์ฐ์ฐ๊ณผ ๋ถ์ ํจ๊ณผ๊ฐ ๋ฐ์ํ ์ ์์์ ์ธ์งํ์.
orElse ์ฌ์ฉ์ด ์ ์ ํ ์ผ์ด์ค
๊ฐ๋จํ๊ณ ๋น์ฉ์ด ๋ฎ์ ๊ฒฝ์ฐ
๋ง์ฝ ๊ธฐ๋ณธ๊ฐ์ด ์ด๋ฏธ ์ค๋น๋์ด ์๊ฑฐ๋, ์์ฑ ๋น์ฉ์ด ๊ทนํ ๋ฎ๊ณ ๋ถ์ ํจ๊ณผ๊ฐ ์ ํ ์๋ค๋ฉด orElse๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง๋ ์ฅ์ ์ด ์๋ค. ์๋ฅผ ๋ค์ด, ์์ ๊ฐ์ด๋ ๋ฏธ๋ฆฌ ๊ณ์ฐ๋ ๊ฐ์ด๋ผ๋ฉด ๊ตณ์ด ์ง์ฐ ํ๊ฐ๋ฅผ ์ํด orElseGet์ ์ธ ํ์๊ฐ ์๋ค.
+ ๊ธฐ๋ณธ๊ฐ ์์ฑ ๋น์ฉ์ด ๋ฎ๋ค๊ณ ํ๋จํ ์ ์๋ ๊ฒฝ์ฐ
- ๋จ์ ๊ฐ์ฒด ์์ฑ
์๋ฅผ ๋ค์ด, ๋จ์ ์์ฑ์๋ฅผ ํธ์ถํ์ฌ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฒฝ์ฐ(์: new User("default", "default@example.com"))์ฒ๋ผ ์ฐ์ฐ์ด ๋งค์ฐ ๊ฐ๋ณ๊ณ ๋ฉ๋ชจ๋ฆฌ ํ ๋น ๋น์ฉ์ด ๋ฏธ๋ฏธํ ๊ฒฝ์ฐ ์ฌ์ฉ์ ๊ณ ๋ คํด๋ณผ ์ ์๋ค. - ๊ณ์ฐ์ด๋ ์ธ๋ถ ์ฐ์ฐ์ด ์๋ ๊ฒฝ์ฐ
๊ธฐ๋ณธ๊ฐ์ ์์ฑํ ๋ ๋คํธ์ํฌ ์์ฒญ, ํ์ผ ์ ์ถ๋ ฅ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ, ๋ณต์กํ ๊ณ์ฐ ๋ฑ์ ๋ฌด๊ฑฐ์ด ์ฐ์ฐ์ด ์ ํ ํฌํจ๋์ด ์์ง ์๋ค๋ฉด, ์ด ๊ฒฝ์ฐ๋ ๊ธฐ๋ณธ๊ฐ ์์ฑ ๋น์ฉ์ด ๋ฎ๋ค๊ณ ๋ณผ ์ ์๋ค. - ๋ถ์ ํจ๊ณผ๊ฐ ์๋ ์์ ๋ฉ์๋
๊ธฐ๋ณธ๊ฐ์ ๋ง๋๋ ๋ฉ์๋๊ฐ ์์ ๋ฉ์๋๋ผ๋ฉด, ์ฆ ์ ๋ ฅ์ด ๊ฐ์ผ๋ฉด ํญ์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ณ ์ธ๋ถ ์ํ๋ ๋ถ์ ํจ๊ณผ(๋ก๊ทธ, ์ํ ๋ณ๊ฒฝ ๋ฑ)๋ฅผ ๋ฐ์์ํค์ง ์๋๋ค๋ฉด ๋น์ฉ ๋ถ๋ด์ด ๋ฎ๋ค๊ณ ํ๋จํ ์ ์๋ค.
7.2. orElseGet ์ฌ์ฉ ์๋๋ฆฌ์ค - ์ง์ฐ ํ๊ฐ
User user = userOpt.orElseGet(() -> createDefaultUser());
- step 1: ๋จผ์ userOpt ๋ด๋ถ์ ๊ฐ์ด ์๋์ง ํ์ธ
- step 2: ๋ง์ฝ userOpt๊ฐ ๊ฐ์ ๊ฐ์ง๊ณ ์๋ค๋ฉด, ๊ทธ ๊ฐ์ด ๋ฐ๋ก ๋ฐํ๋๊ณ ๋๋ค ํจ์๋ ํธ์ถ๋์ง ์์
- step 3: ๋ง์ฝ userOpt๊ฐ ๋น์ด์๋ค๋ฉด, ๋๋ค ํจ์ ๋ด์ createDefaultUser()๊ฐ ํธ์ถ๋์ด ๊ธฐ๋ณธ ์ฌ์ฉ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ํ ๋ฐํ
๋ฐ๋ผ์ orElseGet ์ฌ์ฉ ์, Optional์ด ๋น์ด์์ ๋๋ง createDefaultUser()๊ฐ ์คํ๋์ด ๋ถํ์ํ ์ฐ์ฐ๊ณผ ๋ถ์ ํจ๊ณผ๋ฅผ ํผํ ์ ์๋ค.
orElse Get ์ฌ์ฉ์ด ์ ์ ํ ์ผ์ด์ค
๋น์ฉ๊ณผ ๋ถ์ ํจ๊ณผ๊ฐ ๋์ ๊ฒฝ์ฐ
๊ธฐ๋ณธ๊ฐ์ ์์ฑํ๋ ๊ณผ์ ์ด ๋ณต์กํ๊ฑฐ๋ ์ธ๋ถ ์์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ํน์ ๋ถ์ ํจ๊ณผ๊ฐ ๋ฐ์ํ ์ ์๋ ์ํฉ์์๋ orElseGet์ ์ฌ์ฉํด์ผํ๋ค. orElseGet์ Optional์ด ๋น์ด์์ ๋๋ง Supplier์ ์ฝ๋๋ฅผ ์คํํ๋ฏ๋ก ๋ถํ์ํ ์ค๋ฒํค๋ ๋ฐฉ์ง์ ๋ฉ์๋ ํธ์ถ๋ก ์ธํ ์๋์น ์๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฉ์งํ ์ ์๋ค.
์์ ์์์ createDefaultUser()๊ฐ ๋ง์ฝ ๊ธฐ๋ณธ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ์ด๋ ์ธ๋ถ API ํธ์ถ์ ํฌํจํ๋ค๋ฉด, userOpt์ ๊ฐ์ด ์กด์ฌํ ๋ ํด๋น ๋ฉ์๋๊ฐ ํธ์ถ๋์ง ์๋๋ก ํ๋ ๊ฒ์ด ์ฑ๋ฅ ๋ฉด์์ ์ ๋ฆฌํ ๊ฒ์ด๋ค.
์ฐธ๊ณ ์๋ฃ :
๋ชจ๋ ์๋ฐ ์ธ ์ก์
https://docs.oracle.com/javase/8/docs/api///?java/util/Optional.html
Java Platform SE 8
docs.oracle.com
https://dzone.com/articles/java-8-optional-usage-and-best-practices
Java 8 Optional Usage and Best Practices
Check out this post where we look at Optional, first introduced in Java 8, how to use it, and best practices for implementing it in your existing projects.
dzone.com
https://nipafx.dev/java-value-based-classes/
Value-Based Classes // nipafx
An explanation of value-based classes in Java 8. Why do they exist? What are their limitations? How (not) to use them?
nipafx.dev
https://www.baeldung.com/java-optional-or-else-vs-or-else-get
https://github.com/FasterXML/jackson-modules-java8
GitHub - FasterXML/jackson-modules-java8: Set of support modules for Java 8 datatypes (Optionals, date/time) and features (param
Set of support modules for Java 8 datatypes (Optionals, date/time) and features (parameter names) - FasterXML/jackson-modules-java8
github.com
https://www.codekru.com/java/java-optional-with-jackson