Optional
Java 8์ ์๋ก ์๊ธด ์ธํฐํ์ด์ค๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฉ์๋๊ฐ ๋ฐํํ ๊ฒฐ๊ณผ๊ฐ์ด ์์์ ๋ช
๋ฐฑํ๊ฒ ํํํ ํ์๊ฐ ์๋ ๊ณณ์์ ์ ํ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณต
ํ๊ธฐ ์ํด ์๋ก ์๊ฒจ๋ฌ๋ค.
Java api doc์ API ๋
ธํธ๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ค๋ช
ํ๊ณ ์๋ค. Optional์ ์ฃผ๋ก ๊ฒฐ๊ณผ ์์์ ๋ํ๋ผ ํ์์ฑ์ด ๋ช
ํํ๊ณ null์ ์ฌ์ฉํ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๋ ๋ฉ์๋ ๋ฐํ ์ ํ์ผ๋ก ์ฌ์ฉํ๋๋ก ๊ณ ์๋์๋ค. ์ ํ์ด ์ต์
์ธ ๋ณ์ ์์ฒด๋ null์ด ์๋๋ฏ๋ก ํญ์ ์ต์
์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌ์ผ์ผ ํ๋ค.
Optional์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฌ์ฉํ๊ธฐ
1. Optional ๋ณ์์ null ๋์ Optional.empty() ์ฌ์ฉ
Optional<Car> optionalCar = null;
Optional<Car> optionalCar = Optional.empty();
2. Optional ๊ฐ์ ๊บผ๋ด์ฐ๊ธฐ ์ ์ ๊ฐ์ด ์๋์ง ํ์ธํ๊ธฐ
Optional<Car> optionalCar = Optional.empty();
Car car = optionalCar.get(); //NoSuchElementException ๋ฐ์
Optional์ ๊ฐ์ด ๋น์ด์์ ์๋ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๊ธฐ์ ์ ์กด์ฌํ๋ค๋ ๊ฒ์ ์ฆ๋ช ํด์ผ ํ๋๋ฐ ์ผ๋ฐ์ ์ผ๋ก๋ isPresent()ํ์ get()์ ์ฌ์ฉํ ์ ์์ง๋ง ์ฝ๋๋ ๊ธธ์ด์ง๊ณ ํ๋ฒ์ ์ฌ์ฉํ ์ ์๋ API๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ฌ์ฉํด๋ณด์.
//isPresent - get
if(optionalCar.isPresent()){
return optionalCar.get();
}else {
return null;
}
//orElse
return optionalCar.orElse(null);
//orElseGet
return optionalCar.orElseGet(() -> null);
orElseGet()์ Supplier
๋ฅผ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, ๊ฐ์ด ์์๋์ ํด๋น supplier๊ฐ ์ํ๋๋ค. ํ์ง๋ง orElse()๋ Optional๋ก ๊ฐ์ธ๊ณ ์๋ ๊ฐ์ฒดํ์
์ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ ๊ฐ์ด ์๋๋ผ๋ ๋ด๋ถ๊ฐ ์ํ๋๊ณ ์ฌ์ฉ๋์ง ์๋ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ฒด๋ฅผ ์ง์ฐ๊ฒ ๋์ด ํ์์๋ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ๋ค.
Optional<String> optStr = Optional.of("Hi");
optStr.orElse(new String("hi1"));
optStr.orElseGet(() -> new String("hi1"));
//bytecode
L1
LINENUMBER 9 L1
ALOAD 1
NEW java/lang/String
DUP
LDC "hi"
INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
INVOKEVIRTUAL java/util/Optional.orElse (Ljava/lang/Object;)Ljava/lang/Object;
POP
L2
LINENUMBER 11 L2
ALOAD 1
INVOKEDYNAMIC get()Ljava/util/function/Supplier; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
()Ljava/lang/Object;,
// handle kind 0x6 : INVOKESTATIC
study/OptionalEx.lambda$main$0()Ljava/lang/String;,
()Ljava/lang/String;
]
INVOKEVIRTUAL java/util/Optional.orElseGet (Ljava/util/function/Supplier;)Ljava/lang/Object;
POP
...
private static synthetic lambda$main$0()Ljava/lang/String;
L0
LINENUMBER 11 L0
NEW java/lang/String
DUP
LDC "hi"
INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
ARETURN
MAXSTACK = 3
MAXLOCALS = 0
}
์๋ฅผ ๋ค์ด ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์์ฑํ์๋ optStr์ null์ด ์๋๋ผ new String("hi1")๊ฐ ์คํ๋์ง ์์ ๊ฒ ๊ฐ์ง๋ง ๋ฐ์ดํธ์ฝ๋๋ฅผ ๋ณด๋ฉด ์๋ก ๋ฌธ์์ด์ ์์ฑํ๋ค๊ฐ POPํ๋ ๊ฒ์ ๋ณผ ์ ์๊ณ , orElseGet()์ ์ฐ๋ฆฌ๊ฐ lambda์์ ๋ดค๋๊ฒ์ฒ๋ผ static ๋ฉ์๋๋ก ์์ฑํ์ฌ ํธ์ถ๋๋ ํ์ด๋ฐ์ ์ด๋ฅผ ์คํํด ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋ณผ์์์ด orElse๋ ํ์์๋ ์ค๋ฒํค๋์ ์ฃผ์ํด์ผํ๋ค. ํ์ง๋ง ๋ฐ๋์ ์ด๋ฏธ ์์ฑ๋์ด์๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ๊ฒ์ด๋ผ๋ฉด orElse()๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ์๋ ์๋ค.
//before
if(optionalCar.isPresent()){
return optionalCar.get();
}else {
throw new NoSuchElementException();
}
//after
optionalCar.orElseThrow();
์์ธ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฒฝ์ฐ์๋ isPresent()๋ฅผ ํตํ ์์ธ์ฒ๋ฆฌ๋ณด๋ค๋ orElseThrow()
๋ฅผ ์ด์ฉํ์ฌ ์์ธ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค. ์ธ์๋ก Supplier
๋ฅผ ํตํด ํน์ Exception์ ๋์ง ์ ์๋๋ฐ ์๋ฌด๊ฒ๋ ์ฃผ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก NoSuchElementException์ ๋์ง๋ค.
3. Optional์ด ์์๋๋ง ์ด๋ฅผ ์๋นํ์ฌ ๋ฌด์ธ๊ฐ๋ฅผ ํ ๋๋ ifPresent()๋ฅผ ํ์ฉ
//before
if(optionalCar.isPresent()){
System.out.println(optionalCar.get());
}
//after
optionalCar.ifPresent(System.out::println);
4. ์ปฌ๋ ์
์ Optional๋์ ๋น์ด์๋ ์ปฌ๋ ์
์ ์ฌ์ฉํ์.
//before
List<Car> cars = carFactory.getCars();
return Optional.ofNullable(cars);
//after
List<Car> cars = carFactory.getCars();
return cars != null ? cars : Collections.emptyList();
5. ์ปฌ๋ ์
์ Optional๋ก ๊ฐ์ธ์ง๋ง์.
์ปฌ๋ ์ ์์ฒด๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ผ ํํ์ ๊ฐ์ฒด์ด๊ณ ์ด๋ ์ถฉ๋ถํ API๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ํ๋ฒ๋ Optional๋ก ๊ฐ์ธ๋ฉด ํ์์๋ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ๋ค. JPA์ ๋ฉ์๋๋ฅผ ์์ฑํ ๋๋ JPA์์ฒด์ ์ผ๋ก ๋น์ด์๋ ์ปฌ๋ ์ ์ ๋ฐํํด์ฃผ๋ฏ๋ก Optional๋ก ๊ฐ์ ํ์๊ฐ ์๋ค.
//bad
Optional<List<Car>> findAllByCompanyName(String name);
//good
List<Car> findAllByCompanyName(String name);
6. ์ปฌ๋ ์
,Map์ ์์๋ก Optional์ ์ฌ์ฉํ์ง ๋ง์.
7. ๋จ์ผ ๊ฐ์ ์ป๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก ๋ฉ์๋ ์ฒด์ด๋์ ํ์ง๋ง์.
//bad
String status = "on";
return Optional.ofNullable(status).orElse("PENDING");
//good
status == null ? "PENDING" : status;
Optional์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ฒฐ๊ตญ ๋ํ๋์ ๋ํผ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ฏ๋ก ๋จ์ํ ๋ก์ง์ด๋ผ๋ฉด ๊ทธ๋ฅ ์ฝ๋ฉํ์. ๊ตฌ๊ด์ด ๋ช ๊ด์ด๋ผํ๋ค.
8. Optional์ ํ๋๋ก ์ฌ์ฉํ์ง๋ง์.
public class Car {
private Optional<Navigation> navigation = Optional.empty(); //bad
}
Optional์ ์ ์ด์ ํ๋๋ก ์ฌ์ฉํ ๋ชฉ์ ์ผ๋ก ๋ง๋ค์ด์ง์ง ์์ Serializable๋ ๊ตฌํํ์ง ์์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ์ง์ํด์ผ ํ๋ค.
9. Optional์ ๋ฉ์๋,์์ฑ์ ์ธ์๋ก ์ฌ์ฉํ์ง๋ง์.
//bad
public void render(Optional<Renderer> renderer){
renderer.orElseThrow(() -> new IllegalArgumentException("null ์ผ ์ ์์ต๋๋ค."));
//...
}
//good
public void render(Renderer renderer){
if(renderer == null){
throw new IllegalArgumentException("null ์ผ ์ ์์ต๋๋ค.");
}
//...
}
์ด๋ฌํ ๋ฐฉ๋ฒ์ ๋ถํ์ํ๊ฒ ์ฝ๋๋ฅผ ๋ณต์กํ๊ฒ ํ ๋ฟ์๋๋ผ ์ด๋ฅผ ํธ์ถํ๋ ์ชฝ์์๋ Optional ์์ฑ์ ๊ฐ์ ํ๊ฒ ํ๋ ๊ฒ์ด๋ค. ๋ํ, Optional์ ํ๋์ ๊ฐ์ฒด๋ก ์ด๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ๊ฒฐ์ฝ ๋น์ฉ์ด ์ ๋ ดํ์ง ์๋ค.
10. null์ด ํ์คํ๋ฉด ofNullable()์ด ์๋ of()๋ฅผ ์ฌ์ฉํ์.
//bad
Optional.ofNullable("NULL์ผ ์ ์์ง!");
//good
Optional.of("NULL์ผ ์ ์์ง!");
//ofNullable
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
ofNullable์ ๋ด๋ถ์ ์ผ๋ก ๋ณด๋ฉด ์ผํญ์ฐ์ฐ์๋ฅผ ํตํด ๋น์ด์์ง ์๋ ๊ฒฝ์ฐ of๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋ณผ ์์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ์ฐ์ฐ์ ์กฐ๊ธ์ด๋ผ๋ ์ค์ผ ์ ์๊ธฐ ๋๋ฌธ์ of๋ฅผ ์ฌ์ฉํ์.
11. Optional์ ํ์
์ด Primitiveํ์
์ด๋ฉด OptionalInt,OptionalLong, OptionalDouble์ ์ฌ์ฉํ์
//bad
Optional<Integer> max = Optional.of(10);
for(int i=0; i < max.get(); i++)
//good
OptionalInt max = OptionalInt.of(10);
for(int i=0; i < max.getAsInt(); i++)
//byte code
L0
LINENUMBER 10 L0
BIPUSH 10
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
INVOKESTATIC java/util/Optional.of (Ljava/lang/Object;)Ljava/util/Optional;
ASTORE 0
L1
LINENUMBER 12 L1
BIPUSH 10
INVOKESTATIC java/util/OptionalInt.of (I)Ljava/util/OptionalInt;
ASTORE 1
๋ด๋ถ์ ์ผ๋ก Integer.valueOf()๋ฅผ ํตํด ํ๋ฒ boxing์ด ์ผ์ด๋๋ ๊ฒ์ ๋ณผ ์ ์๊ณ ๋ ์ด๋ฅผ ์ฌ์ฉํ ๋ unboxing์ด ์ผ์ด๋๊ธฐ ๋๋ฌธ์ OptionalInt๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
12. Optional์ ๋ฆฌํดํ๋ ๋ฉ์๋์์ null์ ๋ฆฌํดํ์ง ๋ง์.
public static Optional<String> hi(){
return null;
}
๋น์ฐํ๊ฑฐ์ง๋ง Optional๋ ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ์ null์ ๋ฆฌํด์ด ๊ฐ๋ฅํ๋ฐ ์ด๋ ๊ฒ ๋ฆฌํดํ๊ฒ ๋๋ฉด Optional์ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋ฏธ๊ฐ ์๊ธฐ ๋๋ฌธ์ null์ ๋ฆฌํดํ์ง ๋ง์.
Reference
https://dzone.com/articles/using-optional-correctly-is-not-optional
Last updated