Null ๋ด๋ถ ์ฒ๋ฆฌ
Java 8์ Optional
Optional์ ๋ฑ์ฅ ์ด์ : Null์ ์ธ๋ถ๊ฐ ์๋ ๋ด๋ถ์์ ์ฒ๋ฆฌ
Optional ํด๋์ค๋ NullPointerExceptio ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋์ ๋ ๊ฐ๋ ์ผ๋ก, ์๋ฐ์์ null์ ์ฒ๋ฆฌํ๋ ๋ฒ๊ฑฐ๋ก์์ ํด๊ฒฐํ๊ธฐ ์ํ ๋๊ตฌ์ด๋ค. ์ด์ ์๋ null ์ฒดํฌ๋ฅผ ์ธ๋ถ์์ ์ง์ ์ฒ๋ฆฌํด์ผ ํ์ผ๋, Optional์ ์ฌ์ฉํ๋ฉด null ์ํ๋ฅผ ๊ฐ์ฒด ๋ด๋ถ์์ ์ฒ๋ฆฌํ ์ ์๋ค.
- ๊ตด๋
- ๊ตด๋ ๋ฐ์์ Null ์ฒ๋ฆฌ : ์ธ๋ถ์์ if๋ฌธ์ ์ฌ์ฉํ์ฌ null ๊ฐ์ ์ง์ ํ์ธ
- ๊ตด๋ ์์์ Null ์ฒ๋ฆฌ: Optional ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ด๋ถ์์ null ์ฌ๋ถ๋ฅผ ๋ค๋ฃธ
Optional์ด ์๋ ์์ ์๋ฐ์์ Null ์ฒ๋ฆฌํ๋๋ฒ๊ณผ ์๋ฐ์คํฌ๋ฆฝํธ์์ Null ์ฒ๋ฆฌ ํ๋ ๋ฒ
/* ์ฃผ๋ฌธ */
public class Order {
public Long id;
public Date date;
public Member member;
}
/* ํ์ */
public class Member {
public Long id;
public String name;
public Address address;
}
/* ์ฃผ์ */
public class Address {
public String street;
public String city;
public String zipcode;
}
- ๋ค์๊ณผ ๊ฐ์ด ํด๋์ค๊ฐ ์ฐ๊ฒฐ๋์ด์๋ค๊ณ ๊ฐ์
if (order) {
Member member = order.member;
if (member) {
Address address = member.address;
if (address) {
return address.city;
} else {
return "Seoul";
}
}
}
- Optional์ด ๋ฑ์ฅํ๊ธฐ ์ ์ ์๋ฐ์์๋ `if-else` ๋ฌธ๋ฒ์ ํตํด Null์ ์ฒ๋ฆฌ ํ๋ค.
order?.member?.address?.city ?? "Seoul"
- ์๋ฐ์คํฌ๋ฆฝํธ์์๋ Null Cascading(`?.`)๊ณผ Nullish coalescing(`??`)์ ํตํด Null์ ๋ด๋ถ์์ ์ฒ๋ฆฌํ๋ค.
Optional.ofNullable(order).map((order) -> order.member).map((member) -> member.address).map((address) -> address.city).orElse("Seoul")
- ์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ก์ง๊ณผ ๋ณธ ๋ก์ง์ ๋น๊ตํ์ ๋, Optional์ด ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ํด ๋์จ ๊ฒ์์ ์ดํดํ ์ ์๋ค.
- Null์ ์ธ๋ถ์์ ์ฒ๋ฆฌ ํ์ ๋ ๋ฐ์ํ๋ ๋ฌธ์
- NPE ๋ฐ์์ ๋ํ ์ฒ๋ฆฌ๋ ์ธ๋ถ์ ์์นํ๊ฒ ๋๋ค.
- ์์์ ๋ณธ ๋ฐ์ ๊ฐ์ด Null ์ฒดํฌ ๋ก์ง์ด ์ธ๋ถ์ ์์นํ๊ธฐ์ ๋ก์ง์ ์ ์ง๋ณด์์ฑ๊ณผ ๊ฐ๋ ์ฑ์ด ํ์ ํ ๋จ์ด์ง๋ค.
Optional: ์ํ(๊ฐ์ด ์๊ฑฐ๋ ์๋)๋ฅผ ๊ฐ์ง๋ ๊ฐ์ฒด
Optional์ null ๋์ ๊ฐ์ด ์์ ์๋ ์์ ์๋ ์๋ ์ํ๋ฅผ ๊ฐ์ง ๊ฐ์ฒด์ด๋ค. ์ด๋ฅผ ํตํด null ์ฒ๋ฆฌ๋ฅผ ๋ช ์์ ์ผ๋ก ํ ์ ์๋ค.
- ๋ณ์ ์ ์ธ: `Optional<Member>` Generic ์ ๋ค๋ฆญ์ ํตํด ์ด๋ค ํ์ ์ ๊ฐ์ ๊ฐ์ง ์ ์๋์ง๋ฅผ ๋ช ์ํ๋ค.
- ๊ฐ์ฒด ์์ฑ : `Optional.ofNullable()` ๊ฐ์ด ์์์๋ ์๋ = ๊ฐ์ด ์๋ / ๊ฐ์ ๊ฐ์ง Optional
- `Optional.of()`: ๊ฐ์ ๊ฐ์ง Optional | null์ Optional.of() ์์ ๋ฃ์ด ํธ์ถํ๋ฉด ์ค๋ฅ ๋ฐ์.
- `Optional.empty()`: ๊ฐ์ด ์๋ Optional
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ ๋ชจ๋ ๊ฒ๋ค์ ๊ฐ์ผ๋ก ๋ค๋ฃจ๊ธฐ๋ฅผ ์ํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ Monad๋ผ๋ ๊ฐ๋ ์ด ๋์จ ๊ฒ์ด๊ณ , ํจ์ ์คํ ์ ๋ฐ์ํ๋ ์ค๋ฅ๋ ํ๋์ ์ํ ๊ฐ์ ๊ฐ๋๋ค.
- ํจ์ ์คํ์ ๊ฒฐ๊ณผ๋ ๋ฌด์กฐ๊ฑด ๊ฐ์ผ๋ก ํ๊ธฐ๋์ด์ผ ํ๋ค.
- ์ข ์ด์๋ค ์ํ ๊ณต์์ ์ ์์ ๋ ์ฐ์ฐ ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค๊ณ exception์ด ์ข ์ด์์ ํ์ด๋์ค์ง ์๋๋ค.
Optional ์ฌ์ฉ๋ฒ
Optional<Member> memberExist = Optional.ofNullable(member);
Optional<Member> memerNotExist = Optional.ofNullable(null);
- ๊ฐ์ด ์๋ ๊ฒฝ์ฐ: `.get()` ๋ฉ์๋๋ก ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- ๊ฐ์ด ์๋ ๊ฒฝ์ฐ: `.orElse()` ๋๋ `.orElseThrow()` ๋ก ๊ธฐ๋ณธ๊ฐ์ ์ง์ ํ๊ฑฐ๋ ์์ธ๋ฅผ ๋์ง ์ ์๋ค.
Optional ํ์ฉ๋ฒ
1. .map(): ๊ฐ์ ๋ณํ
public String getCityFromOrder(Order order) {
return Optional.ofNullable(order)
.map(Order::getMember)
.map(Member::getAddress)
.map(Address::getCity)
.orElse("Seoul");
}
2. .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);
}
Optional ์ฌ์ฉ ์์
1. Null ์ฒ๋ฆฌ ์์
Map<Integer, String> cities = new HashMap<>();
cities.put(1, "Seoul");
cities.put(2, "Busan");
cities.put(3, "Daejeon");
- Optional์ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ:
String city = cities.get(4);
int length = (city == null) ? 0 : city.length();
System.out.println("length: " + length);
- Optional์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ:
Optional<String> maybeCity = Optional.ofNullable(cities.get(4));
int length = maybeCity.map(String::length).orElse(0);
System.out.println("length: " + length);
2. ์์ธ ์ฒ๋ฆฌ ์์
List<String> cities = Arrays.asList("Seoul", "Busan", "Daejeon");
- Optional์ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ + Exception ์์ธ์ ๋ฉ์ธ ๋ก์ง์ด ๋ถ๋ฆฌ๋์ง ์์ ๊ฒฝ์ฐ:
String city = null;
try {
city = cities.get(3); // throws exception
} catch (ArrayIndexOutOfBoundsException e) {
// ignore
}
int length = (city == null) ? 0 : city.length(); // null check
System.out.println(length);
- Optional์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ + Exception ์์ธ์ ๋ฉ์ธ ๋ก์ง์ ๋ถ๋ฆฌํ๊ฒฝ์ฐ
public static <T> Optional<T> getAsOptional(List<T> list, int index) {
try {
return Optional.of(list.get(index));
} catch (ArrayIndexOutOfBoundsException e) {
return Optional.empty();
}
}
Optional<String> maybeCity = getAsOptional(cities, 3); // Optional
int length = maybeCity.map(String::length).orElse(0); // null-safe
System.out.println("length: " + length);
3. .ifPresent(): ๊ฐ์ด ์์ผ๋ฉด ์ฒ๋ฆฌ
- `ifPresent(Cosumer<? super T> consumer)` : ์ ํํ๋ Consumer ํจ์ํ ์ธ์๋ฅผ ๋ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
- `ifPresent()`: Optional ์์ ์๋ ๊ฐ์ด ์กด์ฌํ๋์ง ์ฌ๋ถ๋ง ํ๋จํ๋ค.
Optional<String> maybeCity = getAsOptional(cities, 3); // Optional
maybeCity.ifPresent(city -> {
System.out.println("length: " + city.length());
});
Java 9์์ ์ถ๊ฐ๋ Optional
- ifPresentOrElse(): ๊ฐ์ด ์์ผ๋ฉด Consumer ์คํ, ์์ผ๋ฉด Runnable ์คํ
maybeCity.ifPresentOrElse(
city -> System.out.println(city),
() -> System.out.println("City not found")
);
- or(): ๋์ฒด Optional ์ ๊ณต
Optional<String> city = Optional.ofNullable(cities.get(4))
.or(() -> Optional.of("Busan"));
Optional ์ฌ์ฉ ์ ์ฃผ์ํ ์
- Optional์ ๋ฐํํ ๋๋ง ์ฌ์ฉ: ํ๋๋ ํ๋ผ๋ฏธํฐ๋ก Optional์ ์ฌ์ฉํ๋ ๊ฒ์ ์ฑ๋ฅ์์ ๋ฌธ์ ๊ฐ ์์ ์ ์์ผ๋ฏ๋ก, ์ฃผ๋ก ๋ฐํ ๊ฐ์๋ง ์ฌ์ฉํ๋๋ก ํ๋ค.
- null์ด ๋ ์ ์๋ ๊ฐ์ Optional๋ก ๊ฐ์ธ์ ์ฌ์ฉ: Optional์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฌ์ฉํ๋ ค๋ฉด NPE์ ์ํ์ ์ค์ด๊ณ , null์ฒ๋ฆฌ ๋ก์ง์ ๋ช ์์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ค.
โน๏ธ์ฐธ๊ณ
[ASAC 6๊ธฐ ๊ฐ์์๋ฃ]
https://www.daleseo.com/java8-optional-before/
https://www.daleseo.com/java9-optional/
'๐ปDEV-STUDY > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Java]JDBC Driver (0) | 2024.12.10 |
---|---|
[Java] Stream (0) | 2024.10.02 |
[Java] SOLID ์์น (1) | 2024.10.02 |
[Java] Enum (0) | 2024.10.01 |
[Java] Interface / Abstract Class (1) | 2024.10.01 |