๋ฐฑ๊ธฐ์ ๋์ <๋ ์๋ฐ, Java 8>๋ฅผ ๋ณด๊ณ ๊ณต๋ถํ ๋ด์ฉ์ ๊ธฐ๋กํฉ๋๋ค.
2. ์ ์ ํ ๊ฐ์ด ์์ ๊ฒฝ์ฐ, ๋ฆฌํด ๋ฐฉ๋ฒ๋ค
๋ฉ์๋ ์์
์ค์ ๊ฐ์ ์ ๋๋ก ๋ฆฌํดํ๊ธฐ ํ๋ ๊ฒฝ์ฐ, ๊ฐ๋ฐ์๊ฐ ์ ํํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋๋ต ๋ค์๊ณผ ๊ฐ์๋ค.
4-1. ๋ฆฌํด ๊ฐ์ผ๋ก๋ง ์ฐ๊ธฐ๋ฅผ ๊ถ์ฅํ๋ค.
public class Food {
private int id;
private String name;
private boolean isLiked;
private String bestRestaurantName;
// return type ์ผ๋ก๋ง ์ฌ์ฉํ๋ค.
public Optional<String> getBestRestaurantName() {
return Optional.ofNullable(bestRestaurantName);
}
}
// ์ํฐ ํจํด
public class Food {
private int id;
private String name;
private boolean isLiked;
// 3. ์ธ์คํด์ค ํ๋ ํ์
์ผ๋ก ์ฐ์ง ์๋๋ค.
private Optional<String> bestRestaurantName;
public void setBestRestaurantName(Optional<String> bestRestaurantName) {
// null ์ ๋ํด์ ์ฒ๋ฆฌํ ๋ NPE ๋ฐ์ ์ฐ๋ ค
// ์คํ๋ ค ๋ ๋ณต์กํด์ง๋ค.
if (baseRestaurantName == null) {
...
}
}
}
4-2. Optional ์ ๋ฆฌํดํ๋ ๋ฉ์๋์์ null ์ ๋ฆฌํดํ์ง ๋ง์.
public class Food {
private int id;
private String name;
private boolean isLiked;
private String bestRestaurantName;
public Optional<String> getBestRestaurantName() {
// return null; // ๊ธ์ง!
return Optional.empty();
}
}
4-3. Primitive type ์ ์ํ Optional ์ด ๋ณ๋๋ก ์กด์ฌํ๋ค.
@Test
void primitiveTypeOptionalTest() {
OptionalInt optionalInt = OptionalInt.of(10);
optionalInt.ifPresent(System.out::println);
OptionalLong optionalLong = OptionalLong.of(10);
if (optionalLong.isPresent()) {
assertThat(optionalLong.getAsLong()).isEqualTo(10);
}
}
4-4. Collection, Map, Stream Array, Optional ๋ฑ ์ด๋ฏธ ์ปจํ
์ด๋ ํ์
์ผ๋ก ์กด์ฌํ๋ฉฐ ๋น์ด์๋์ง ์ ์ ์๋ ํ์
๋ค์ Optinal ๋ก ๊ตณ์ด ๊ฐ์ธ์ง ์๋๋ค.
@Test
void optional์ด_ํ์์์๋_๋๋ฒ๊ฐ์ธ์ง_๋ง์() {
Optional<Optional<String>> opt = Optional.ofNullable(null); // not recommended
Optional<String> s = opt.get(); //NPE ๋ฐ์
String s1 = s.get();
Optional<List<Food>> optionalFoods = Optional.of(asianFoods); // not recommended
asianFoods.isEmpty(); // recommended
}
public class OptionalApiTest {
private List<Food> asianFoods = new ArrayList<>(){{
add(new Food(1, "๋น๋น๋ฐฅ", true));
add(new Food(2, "์ค๋ ํ", false));
add(new Food(3, "๋ถ๋ญ๋ณถ์๋ฉด", true));
add(new Food(4, "๋ก๋ณถ์ด", true));
add(new Food(5, "๊ถ์ค๋ก๋ณถ์ด", false));
add(new Food(6, "์๋๊ตญ", false));
}};
@Test
void optionalOf๋ฅผ์ด์ฉํ์ฌ_Optional์_๋ง๋ค์์๋ค() {
Optional<Food> optionalOf = Optional.of(new Food(1, "๋ผ๋ฉด", true));
Optional<Food> optionalNullable = Optional.ofNullable(null);
Optional<Food> optionalEmpty = Optional.empty();
}
@Test
void optionalIsPresent๋ฅผ_์ด์ฉํ์ฌ_๊ฐ์_์กด์ฌ์ฌ๋ถ๋ฅผ_ํ์ธํ๋ค() {
Optional<Food> optionalOf = Optional.of(new Food(1, "๋ผ๋ฉด", true));
Optional<Food> optionalNullable = Optional.ofNullable(null);
Optional<Food> optionalEmpty = Optional.empty();
assertThat(optionalOf.isPresent()).isTrue();
assertThat(optionalNullable.isPresent()).isFalse();
assertThat(optionalEmpty.isEmpty()).isTrue();
}
@Test
void optionalGet์_์ด์ฉํ์ฌ_๊ฐ์_๊บผ๋ธ๋ค() {
Optional<Food> optionalRamen = Optional.of(new Food(1, "๋ผ๋ฉด", true));
if (optionalRamen.isPresent()) {
Food ramen = optionalRamen.get();
assertThat(ramen).isEqualTo(new Food(1, "๋ผ๋ฉด", true));
}
}
@Test
void null๊ฐ์_Getํ๋ ค๊ณ ํ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
Optional<Food> optionalNullable = Optional.ofNullable(null);
assertThatThrownBy(() -> optionalNullable.get())
.isInstanceOf(NoSuchElementException.class);
}
@Test
void empty๊ฐ์_Getํ๋ ค๊ณ ํ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
Optional<Food> optionalEmpty = Optional.empty();
assertThatThrownBy(() -> optionalEmpty.get())
.isInstanceOf(NoSuchElementException.class);
}
@Test
void ifPresentConsumer๋ก_๋ฐํ๊ฐ_์์ด_ํ์๋ฅผ_์ํํ๋ค() {
Optional<Food> optionalRamen = Optional.of(new Food(1, "๋ผ๋ฉด", true));
optionalRamen.ifPresent(f -> System.out.println(f.getName()));
//๋ผ๋ฉด
}
@Test
void orElse๋_๋ฌด์กฐ๊ฑด_์คํ๋๋ฏ๋ก_์ด๋ฏธ_๋ฐํํ _๊ฐ์ด_์์๋_์ฌ์ฉํ๋ฉด_์ข๋ค() {
Optional<Food> optionalEmpty = Optional.empty();
Food food = optionalEmpty.orElse(createNewFood());
assertThat(food).isEqualTo(new Food(1, "๋ผ๋ฉด", true));
System.out.println("=====");
Optional<Food> optionalOf = Optional.of(new Food(1, "๋ผ๋ฉด", true));
Food food2 = optionalOf.orElse(createNewFood());
assertThat(food2).isEqualTo(new Food(1, "๋ผ๋ฉด", true));
//new food created
//=====
//new food created
}
@Test
void orElseGet์_์์๋๋ง_์คํ๋๋ฏ๋ก_์๋กญ๊ฒ_๋ง๋ค์ด์ค๋_์ฌ์ฉํ๋ฉด_์ข๋ค() {
Optional<Food> optionalEmpty = Optional.empty();
Food food = optionalEmpty.orElseGet(() -> createNewFood()); //supplier
assertThat(food).isEqualTo(new Food(1, "๋ผ๋ฉด", true));
System.out.println("=====");
Optional<Food> optionalOf = Optional.of(new Food(1, "๋ผ๋ฉด", true));
Food food2 = optionalOf.orElseGet(OptionalApiTest::createNewFood); //supplier
assertThat(food2).isEqualTo(new Food(1, "๋ผ๋ฉด", true));
//new food created
//=====
}
@Test
void orElseThrow๋_์๋๊ฒฝ์ฐ_์๋ฌ๋ฅผ_๋์ง๋ค() {
Optional<Food> optionalEmpty = Optional.empty();
assertThatThrownBy(() -> optionalEmpty.orElseThrow(() -> new NullPointerException()))
.isInstanceOf(NullPointerException.class);
Optional<Food> optionalEmpty2 = Optional.empty();
assertThatThrownBy(() -> optionalEmpty2.orElseThrow(NullPointerException::new))
.isInstanceOf(NullPointerException.class);
}
@Test
void filter๋ฅผ_์ด์ฉํ์ฌ_optional_๊ฐ์_๊ฑธ๋ฌ๋ผ์์๋ค() {
Optional<Food> first = asianFoods.stream()
.filter(f -> f.getId() > 2)
.findFirst();
Food food = first.filter(f -> f.getId() == 3).orElseThrow();
assertThat(food.getName()).isEqualTo("๋ถ๋ญ๋ณถ์๋ฉด");
}
@Test
void flatMap์ผ๋ก_Optional์_๋ค์ด์๋๊ฐ์_๋ณํํ ์์๋ค() {
Optional<Food> first = asianFoods.stream()
.filter(f -> f.getId() > 2)
.findFirst();
Optional<String> filtered = first.map(f -> f.getBestRestaurantName()).orElse(Optional.empty());
assertThat(filtered).isEmpty();
Optional<String> filtered2 = first.flatMap(f -> f.getBestRestaurantName());
assertThat(filtered2).isEmpty();
}
private static Food createNewFood() {
System.out.println("new food created");
return new Food(1, "๋ผ๋ฉด", true);
}
}