# Optional
# 1. Optional ์๊ฐ
๋น์ด์์ ์๋ ์๊ณ , ์ด๋ ํ ๊ฐ ํ๋๋ง ๋ด๊ณ ์์์๋ ์๋ ์ธ์คํด์ค์ ํ์
Optional์ด ๊ฐ์ฒด๋ฅผ ๊ฐ์ธ๋ ๊ตฌ์กฐ
# 1-1. ๋ฑ์ฅ๋ฐฐ๊ฒฝ : ์ฐธ์กฐํ ๋ฉค๋ฒ๋ณ์ ์ NPE
1๏ธโฃ null ๊ด๋ จ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด
- ๋ฐํ์์ NPE(NullPointerException)๋ผ๋ ์์ธ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค.
- NPE ๋ฐฉ์ด๋ฅผ ์ํด์ ๋ค์ด๊ฐ null ์ฒดํฌ ๋ก์ง ๋๋ฌธ์ ์ฝ๋ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง ๋ณด์์ฑ์ด ๋จ์ด์ง๋ค.
/* OnlineClass.java */
...
public Progress progress;
public Progress getProgress() {
return progress;
}
public void setProgress(Progress progress) {
this.progress = progress;
}
...
/* OptionalTestApp.java */
// ์ด์์ํฉ โ ์ฐธ์กฐํ ๋ฉค๋ฒ ๋ณ์ ์ฌ์ฉ ์ ์ด๊ธฐํ ๋์ง ์์ null ๊ฐ์ ์ฐธ์กฐ ํ ์ ์๋ค.
OnlineClass spring_boot = new OnlineClass(1, "spring boot", true);
// NullPointExecption ๋ฐ์
Duration studyDuration = spring_boot.getProgress().getStudyDuration();
System.out.println(studyDuration);
2๏ธโฃ ์๋ฐ8 ์ด์ ํด๊ฒฐ๋ฐฉ๋ฒ
// ๋ฐฉ๋ฒ1. ์ฌ์ ์ null์ ์ฒดํฌ โ ์๋ฌ์ ๋ํ ์คํํธ๋ ์ด์ค๋ฅผ ๋ฝ๋ ๊ฒ ๋ํ ๋ฆฌ์์ค ๋ญ๋น
public Progress getProgress() {
if (progress == null) {
throw new IllegalStateException();
}
return progress;
}
// ๋ฐฉ๋ฒ2. ํด๋ผ์ด์ธํธ ์ฝ๋์์ null์ ํ์ธ โ ์ด๋ ํด๋ผ์ด์ธํธ์์ ๋์น๋ค๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์์.
Progress progress = spring_boot.getProgress();
if (progress != null) {
System.out.println(progress.getStudyDuration());
}
์ด๋ ๊ฒ if๋ฌธ์ผ๋ก null์ ์ฒดํฌํ๊ฒ ๋๋ฉด get method๊ฐ ์ฌ์ฉ๋๋ ๋งํผ null ์ฒดํฌ๋ฅผ ํด์ค์ผํ๋ ๋ฌธ์ ๊ฐ ์๋ค. ๋น์ฆ๋์ค ๋ก์ง๋ณด๋ค null ์ฒดํฌ ์ฝ๋๊ฐ ๊ธธ์ด์ง๋ ๋ง๋ฒ์ด ๋ฐ์ํ๋ค.
3๏ธโฃ ์๋ฐ8 : ๊ณ ํต์ค๋ฌ์ด null ์ฒ๋ฆฌ๋ฅผ Optional์ ์์
- ์ค์นผ๋ผ๋ ํ์ค์ผ๊ณผ ๊ฐ์ ํจ์ํ ์ธ์ด๋ค์ ์ ํ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก null ๋ฌธ์ ์ ํด๊ฒฐ
- ์๋ฐ๊ฐ
์กด์ฌํ์ง ์๋ ๊ฐ
์ ํํํ๊ธฐ ์ํด์ null์ ์ฌ์ฉํ๋ค๋ฉด, ์ด ํจ์ํ ์ธ์ด๋ค์์กด์ฌํ ์ง ์ ํ ์ง ๋ชจ๋ฅด๋ ๊ฐ
์ ํํํ ์ ์๋ ๋ณ๊ฐ์ ํ์ ์ ๊ฐ์ง๊ณ ์๋ค. - ์ด ์ปจ์ ์ ๋ชจํฐ๋ธ๋ก Optional ํด๋์ค ๋์
4๏ธโฃ Optional ์ฅ์
- NPE๋ฅผ ์ ๋ฐํ ์ ์๋ null์ ์ง์ ๋ค๋ฃจ์ง ์์๋ ๋๋ค.
- ์๊ณ ๋กญ๊ฒ null ์ฒดํฌ๋ฅผ ์ง์ ํ์ง ์์๋ ๋๋ค.
- ๋ช ์์ ์ผ๋ก ํด๋น ๋ณ์๊ฐ null์ผ ์๋ ์๋ค๋ ๊ฐ๋ฅ์ฑ์ ํํํ ์ ์๋ค. (๋ฐ๋ผ์ ๋ถํ์ํ ๋ฐฉ์ด ๋ก์ง์ ์ค์ผ ์ ์๋ค!)
# 1-2. Optional ์ฃผ์ ์ฌํญ
1๏ธโฃ Optional์ returnย type ์๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
/* OnlineClass.java */
...
// getter ์ return tpye ์ Optional๋ก ๋ช
์
public Optional<Progress> getProgress() {
return Optional.ofNullable(progress);
}
...
๋ฉ์๋
Parameter
๋ก Optional์ ์ ์ํด๋๋ฉด NPE ๋ฐ์ ๊ฐ๋ฅ์ฑ ์์.// Parameter์ Optional ์ ์ public void setProgress(Optional<Progress> progress) { progress.ifPresent(p -> this.progress = p); } // ํธ์ถ ์ null.ifPresent(...) โ NPE ๋ฐ์ {๊ฐ์ฒด}.setProgress(null);
null ์ฒดํฌ๋ฅผ ํด์ฃผ๋ฉด NPE ๋ฐ์์ ๋ง์ ์ ์์ง๋ง, Optional์ ์ฌ์ฉํ๋ ์๋ฏธ๊ฐ ์์ด์ง๋ค.
Map์ Key
์ Optional์ ์ฌ์ฉํ๋ฉด ์๋๋ค.- Map ์ธํฐํ์ด์ค๋ Key๊ฐ Null์ผ ์ ์๋ค.
์ธ์คํด์ค ํ๋
์ Optional์ ์ง์ํ์.- ์ฌ์ฉํ ์ ์์ง๋ง, ๋๋ฉ์ธ ํด๋์ค ์ค๊ณ ๋ฌธ์ ์ด๋ค.
- ํ๋๊ฐ ์์ ์ ๋, ์์ ์ ๋ ์๋ค๋ ์๋ฏธ
ํด๊ฒฐ ๋ฐฉ์
: ์์/ํ์ ํด๋์ค๋ก ๋ถ๋ฆฌ or Delegation ์ฌ์ฉ ๊ถ์ฅ
2๏ธโฃ Null ํ์ฉ ์ฌ๋ถ์ ๋ฐ๋ผ
ofNuallable
์ ๊ณ ๋ คํด๋ณด์.
ofNullable
: null์ ๋ฐ๋ ๊ฒ์ ํ์ฉํ๋ค.of
: null์ ๋ฐ์ผ๋ฉด NPE ๋ฐ์
NPE๋ฅผ ๋ฐ์์ํค์ง ์์ผ๋ฉด์ null์ ๋ฐ์ ์ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ์.
3๏ธโฃ
primitiveType
์ Optional์ ์ ์ ํ ์ฌ์ฉ
Optional.of(10); // ๋ด๋ถ์์ boxing, unBoxing์ด ์ด๋ฃจ์ด์ง๋ค. โ ์ฑ๋ฅ ์ ํ
OptionalInt.of(10); // ๊ฐ primitive Type์ ๋ง๋ ํด๋์ค๋ฅผ ์ ๊ณต
4๏ธโฃ Optional return ๋ฉ์๋์์
null์ return ํ์ง ๋ง์
// ์งํฅํด์ผํ ์ฝ๋ โ NPE ๋ฐ์ ๊ฐ๋ฅ
public Optional<Progress> getProgress() {
return null;
}
// ์ข์ ์ฝ๋
public Optional<Progress> getProgress() {
return Optional.empty();
}
5๏ธโฃ
Collection, Map, Stream Array, Optional
์ Optional๋ก ๊ฐ์ธ์ง ๋ง ๊ฒ
- ์ปจํ ์ด๋ ์ฑ๊ฒฉ์ ์ธ์คํด์ค๋ค์ ์ด๋ฏธ ๋น์ด์๋ค๋ ๊ฒ์ ํํํ ์ ์๋ค.
- ๊ทธ๋ฌ๋ฏ๋ก Optional๋ก ๊ฐ์ธ๋ฉด ๋ ๋ฒ ๊ฐ์ธ๊ฒ ๋จ. โ ์๋ฏธ๊ฐ ์๋ค!
# 2. Optional API
# 2-1. Optional ๋ง๋ค๊ธฐ
1๏ธโฃ ์ ์ธํ๊ธฐ
์ ๋ค๋ฆญ์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์, ๋ณ์๋ฅผ ์ ์ธํ ๋ ๋ช ๊ธฐํ ํ์ ํ๋ผ๋ฏธํฐ์ ๋ฐ๋ผ์ ๊ฐ์ ์ ์๋ ๊ฐ์ฒด์ ํ์ ์ด ๊ฒฐ์ ๋๋ค.
Optional<Order> maybeOrder; // Order ํ์
์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ์ ์๋ Optional ํ์
์ ๋ณ์
Optional<Member> optMember; // Member ํ์
์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ์ ์๋ Optional ํ์
์ ๋ณ์
Optional<Address> address; // Address ํ์
์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ์ ์๋ Optional ํ์
์ ๋ณ์
๋ณ์๋ช
์ ๊ทธ๋ฅ ํด๋์ค ์ด๋ฆ์ ์ฌ์ฉํ๊ธฐ๋ ํ์ง๋ง maybe
๋ opt
์ ๊ฐ์ ์ ๋์ด๋ฅผ ๋ถ์ฌ์ Optional ํ์
์ ๋ณ์๋ผ๋ ๊ฒ์ ์ข ๋ ๋ช
ํํ ๋ํ๋ด๊ธฐ๋ ํ๋ค.
2๏ธโฃ ๊ฐ์ฒด ์์ฑํ๊ธฐ
Optional.empty()
null์ ๋ด๊ณ ์๋, ํ ๋ง๋๋ก ๋น์ด์๋ Optional ๊ฐ์ฒด๋ฅผ ์ป์ด์จ๋ค.
์ด ๋น์ด์๋ ๊ฐ์ฒด๋ Optional ๋ด๋ถ์ ์ผ๋ก ๋ฏธ๋ฆฌ ์์ฑํด๋์ ์ฑ๊ธํด ์ธ์คํด์ค์ด๋ค.
Optional<Member> maybeMember = Optional.empty();
Optional.of()
null์ด ์๋ ๊ฐ์ฒด๋ฅผ ๋ด๊ณ ์๋ Optional ๊ฐ์ฒด๋ฅผ ์์ฑ
null์ด ๋์ด์ฌ ๊ฒฝ์ฐ, NPE๋ฅผ ๋์ง๊ธฐ ๋๋ฌธ์ย ์ฃผ์ํด์ ์ฌ์ฉํ์!
Optional<Member> maybeMember = Optional.of(aMember);
Optional.ofNullable()
- null์ธ์ง ์๋์ง ํ์ ํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ๋ด๊ณ ์๋ Optional ๊ฐ์ฒด๋ฅผ ์์ฑ
Optional.empty()
์ยOptional.ofNullable(value)
๋ฅผ ํฉ์ณ๋์ ๋ฉ์๋๋ก ์ดํดํ๋ฉด ํธํ๋ค.- null์ด ๋์ด์ฌ ๊ฒฝ์ฐ, NPE๋ฅผ ๋์ง์ง ์๊ณ ย
Optional.empty()
์ ๋์ผํ๊ฒ ๋น์ด ์๋ Optional ๊ฐ์ฒด๋ฅผ ์ป์ด์จ๋ค. - ํด๋น ๊ฐ์ฒด๊ฐ null์ธ์ง ์๋์ง ์์ ์ด ์๋ ์ํฉ์์๋ ์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์.
Optional<Member> maybeMember = Optional.ofNullable(aMember); Optional<Member> maybeNotMember = Optional.ofNullable(null);
# 2-2. Optional ๊ฐ ์ฌ๋ถ ํ์ธ
- isPresent()
- isEmpty()
Java11๋ถํฐ ์ ๊ณต
# 2-3. Optional ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
์๋ ๋ฉ์๋๋ค์ ๋ชจ๋ Optional์ด ๋ด๊ณ ์๋ ๊ฐ์ฒด๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ ๋์ผํ๊ฒ ํด๋น ๊ฐ์ ๋ฐํ ํ์ง๋ง, Optional์ด ๋น์ด์๋ ๊ฒฝ์ฐ(null์ ๋ด๊ณ ์๋ ๊ฒฝ์ฐ), ๋ค๋ฅด๊ฒ ์๋
get()
- null์ผ ๊ฒฝ์ฐ NoSuchElementException ๋ฐ์
๊ฐ๊ธ์ ์ฌ์ฉ์ ํ์ง ์๋ ๊ฒ์ ๊ถ์ฅ.
orElse(T other)
- null์ผ ๊ฒฝ์ฐ ๋์ด์จ ์ธ์(T) ๋ฅผ ๋ฐํ
T๋ ์ธ์คํด์ค ํ์
orElseGet(Supplier<? extends X>)
- null์ผ ๊ฒฝ์ฐ ๋์ด์จ ํจ์ํ ์ธ์๋ฅผ ํตํด ์์ฑ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํ
orElse(T other)
์ ๊ฒ์ผ๋ฅธ ๋ฒ์ - ๋น์ด์๋ ๊ฒฝ์ฐ์๋ง ํจ์๊ฐ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ย
orElse(T other)
ย ๋๋น ์ฑ๋ฅ์ ์ด์ ์ ๊ธฐ๋ํ ์ ์์.
orElseThrow(Supplier<? extends X> exceptionSupplier)
- null์ผ ๊ฒฝ์ฐ ๋์ด์จ ํจ์ํ ์ธ์๋ฅผ ํตํด ์์ฑ๋ ์์ธ๋ฅผ ๋์ง๋ค.
default : NoSuchElementException
ifPresent(Consumer)
- ๊ฐ์ด ์๋ ๊ฒฝ์ฐ ๊ฐ์ ๊ฐ์ง๊ณ Consumer ํจ์ ๋์
// 1. get() OnlineClass onlineClass = optional.get(); // NoSuchElementException ๋ฐ์ // 2. isPresent() + get() = ๋จผ์ ํ์ธํ ๊บผ๋ธ๋ค ๋ฒ๊ฑฐ๋กญ๋ค if (optional.isPresent()) { OnlineClass onlineClass = optional.get(); System.out.println(onlineClass.getTitle()); } // 3. ifPresent(Consumer) = ๊ฐ์ด ์๋ ๊ฒฝ์ฐ๋ง ํจ์๊ฐ ๋์ํ๋ค! optional.ifPresent(oc -> System.out.println(oc.getTitle()));
# 2-4. Optional ํํฐ/๋ณํ
- Optional map(Function)
- Optional filter(Predicate)
- Optional flatMap(Function) : Optional ์์ ์ธ์คํด์ค๊ฐ Optional์ธ ๊ฒฝ์ฐ ์ฌ์ฉ
1๏ธโฃ map์ ํ์ฉํด๋ณด์.
์ฃผ๋ฌธ์ ํ ํ์์ด ์ด๊ณ ์๋ ๋์๋ฅผ ๋ฐํํ๋ ๋ฉ์๋๋ค. (๊ธฐ๋ณธ๊ฐ์ Seoul ์ด๋ค.)
// AS-WAS
public String getCityOfMemberFromOrder(Order order) {
if (order == null) {
return "Seoul";
}
Member member = order.getMember();
if (member == null) {
return "Seoul";
}
Address address = member.getAddress();
if (address == null) {
return "Seoul";
}
String city = address.getCity();
if (city == null) {
return "Seoul";
}
return city;
}
์ฌ๋ฐฉ์์ return ํด์ค์ผ ํ์ฌ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๊ณ , ์ ์ง๋ณด์๊ฐ ์ข์ง ์์ ์ฝ๋๊ฐ ์๋ค.
// TO-BE
public String getCityOfMemberFromOrder(Order order) {
return Optional.ofNullable(order)
.map(Order::getMember)
.map(Member::getAddress)
.map(Address::getCity)
.orElse("Seoul");
}
์ ํต์ ์ธ NPE ๋ฐฉ์ด ํจํด์ ๋นํด ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ๋ช ํํด์ง๋ค!
์ฐ์ ๊ธฐ์กด์ ์กด์ฌํ๋ ์กฐ๊ฑด๋ฌธ๋ค์ด ๋ชจ๋ ์ฌ๋ผ์ง๊ณ Optional์ ์๋ คํ(fluent) API์ ์ํด์ ๋จ์ํ ๋ฉ์๋ ์ฒด์ด๋์ผ๋ก ๋ชจ๋ ๋์ฒด๋๋ค.
2๏ธโฃ map : ๋ฉ์๋ ์ฒด์ด๋ ์ค๋ช
ofNullable()
ย ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ Order ๊ฐ์ฒด๋ฅผ Optional๋ก ๊ฐ์ผ๋ค.- ํน์ Order ๊ฐ์ฒด๊ฐ null์ธ ๊ฒฝ์ฐ๋ฅผ ๋๋นํ์ฌย
of()
ย ๋์ ์ยofNullable()
์ ์ฌ์ฉํ๋ ๊ฒ.
- ํน์ Order ๊ฐ์ฒด๊ฐ null์ธ ๊ฒฝ์ฐ๋ฅผ ๋๋นํ์ฌย
- 3๋ฒ์ย
map()
ย ๋ฉ์๋์ ์ฐ์ ํธ์ถ์ ํตํด์ Optional ๊ฐ์ฒด๋ฅผ 3๋ฒ ๋ณํํ๋ค.- ๋งค ๋ฒ ๋ค๋ฅธ ๋ฉ์๋ ๋ ํผ๋ฐ์ค๋ฅผ ์ธ์๋ก ๋๊ฒจ์ Optional์ ๋ด๊ธด ๊ฐ์ฒด์ ํ์ ์ ๋ฐ๊ฟ์ค๋ค.
Optional<Order>
ย ->ยOptional<Member>
ย ->ยOptional<Address>
ย ->ยOptional<String>
- ๋ง๋ฌด๋ฆฌ ์์
์ผ๋กย
orElse()
ย ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ด ์ ๊ณผ์ ์ ํตํด ์ป์ Optional์ด ๋น์ด์์ ๊ฒฝ์ฐ, ๋ํดํธ๋ก ์ฌ์ฉํ ๋์ ์ด๋ฆ์ ์ธํ ํด์ฃผ๋ฉด ๋๋ค.
3๏ธโฃ fileter๋ฅผ ์ฌ์ฉํด๋ณด์.
Java8 ์ด ์ ์ NPE ๋ฐฉ์ง๋ฅผ ์ํด์ ๋ค์๊ณผ ๊ฐ์ด null ์ฒดํฌ๋ก ์์ํ๋ if ์กฐ๊ฑด๋ฌธ ํจํด์ ๋ง์ด ์ฌ์ฉํด์๋ค.
if (obj != null && obj.do() ...)
์๋ฅผ ๋ค์ด, ์ฃผ์ด์ง ์๊ฐ(๋ถ) ๋ด์ ์์ฑ๋ ์ฃผ๋ฌธ์ ํ ๊ฒฝ์ฐ์๋ง ํด๋น ํ์ ์ ๋ณด๋ฅผ ๊ตฌํ๋ ๋ฉ์๋๋ฅผ ์ ํจํด์ผ๋ก ์์ฑํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
public Member getMemberIfOrderWithin(Order order, int min) {
if (order != null && order.getDate().getTime() > System.currentTimeMillis() - min * 1000) {
return order.getMember();
}
}
- if ์กฐ๊ฑด๋ฌธ ๋ด์ null ์ฒดํฌ์ ๋น์ง๋์ค ๋ก์ง์ด ํผ์ฌ๋์ด ์์ด์ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๋ค.
- ๊ฒ๋ค๊ฐ null์ ๋ฆฌํดํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฉ์๋ ํธ์ถ๋ถ์ NPE ์ํ์ ์ ํํ๊ณ ์๋ค.
filter๋ฅผ ์ ์ฉํด๋ณด์.
public Optional<Member> getMemberIfOrderWithin(Order order, int min) {
return Optional.ofNullable(order)
.filter(o -> o.getDate().getTime() > System.currentTimeMillis() - min * 1000)
.map(Order::getMember);
}
- if ์กฐ๊ฑด๋ฌธ ์์ด ๋ฉ์๋ ์ฐ์ ํธ์ถ๋ง์ผ๋ก๋ ์ข ๋ ์ฝ๊ธฐ ํธํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
- ๋ฟ๋ง ์๋๋ผ, ๋ฉ์๋์ ๋ฆฌํด ํ์ ์ Optional๋ก ์ฌ์ฉํจ์ผ๋ก์จ ํธ์ถ์์๊ฒ ํด๋น ๋ฉ์๋๊ฐ null์ ๋ด๊ณ ์๋ Optional์ ๋ฐํํ ์๋ ์๋ค๋ ๊ฒ์ ๋ช ์์ ์ผ๋ก ์๋ ค์ค๋ค.
# Reference
โ Stream Date์ Time โ