[Java] Java Optional
by 배부른코딩로그Optional 클래스는 고질적인 문제인 NullpointerException를 해결하기 위해 추가되었다.
import java.util.Optional;
of, ofNullable로 객체 감싸기
Optional로 감싸기 위해서는 Optional에서 static 메서드로 제공하는 of 와 ofNullable 를 사용할 수 있다.
- Optional.of(T value)는 인자로서 null값을 허용하지 않는다.
@Test
public void optional_of_with_not_null() {
Optional<String> optionalName = Optional.of("배부른코딩로그");
assertEquals("Optional[배부른코딩로그]", optionalName.toString());
}
@Test
public void optional_of_with_null() {
Optional<String> optionalNullValue = Optional.of(null);
assertThrows(NullPointerException.class, () -> {
optionalNullValue.toString();
});
}
- Optional.ofNullable(T value)은 null값을 허용한다.
@Test
public void optional_ofNullable_with_not_null() {
Optional<String> optionalName = Optional.ofNullable("배부른코딩로그");
assertEquals(optionalName.toString());
}
@Test
public void describe_optional_ofNullable() {
Optional<String> optionalNull = Optional.ofNullable(null);
assertEquals("Optional.empty", optionalNull.toString());
}
- isPresent()를 통해 현재 Optional객체가 보유한 값이 null인지 아닌지 확인도 가능하다.
@Test
public void describe_optional_ofNullable() {
Optional<String> optionalName = Optional.of("배부른코딩로그");
assertTrue(optionalName.isPresent());
Optional<String> optionalMotto = Optional.of(null);
assertFalse(optionalMotto.toString());
}
Optional의 isPresent메서드는 아래와 같은 if를 이용한 null값 체크를 대체할 수 있다.
if (name != null) {
System.out.println(name.substring(3));
}
if null check가 좋지 않은 이유는 크게 두 가지다
- 각 변수마다 null값을 체크해야 되기 때문에 개발자의 실수(NullPointerException)를 유발할 가능성이 높아진다.
- 코드가 길어짐에 따라 가독성이 떨어진다.
따라서, Optional 방식은 코드를 안전하게 만들뿐만 아니라 가독성면에서도 좋다고 볼 수 있다.
orElse, orElseGet 활용하기
if 에서 null값이 아닌 경우의 처리를 else 키워드로 해결하지만, Optional의 경우 orElse로 간단하게 해결할 수 있다.
@Test public void when_orElse_with_null() {
String nullName = null;
String name = Optional.ofNullable(nullName)
.orElse("배부른코딩로그");
assertEquals("배부른코딩로그", name);
}
Optional 값을 가져올 때 자주 사용되는 메서드는 orElseGet 와 orElse 다. 두 메서드 모두 null값 체크와 동시에 null값일 때 추가적인 처리가 가능하다. 다만, 추가적으로 처리하는 부분이 함수인 경우 주의가 필요하다. orElseGet은 Optional 값이 null일 경우에만 orElseGet에 주어진 함수를 실행하지만, orElse는 null값 유무와 상관없이 실행된다. 이 부분을 신경쓰지 않으면 성능 이슈가 발생할 수 있기 때문에 주의가 필요하다.
public String getMyDefault()
System.out.println("Getting Default Value");
return "Default Value";
}
@Test
public void whenOrElseGetAndOrElseOverLap() {
String text = null;
System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text)
.orElseGet(this::getMyDefault);
assertEquals("Default Value", defaultText);
System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text)
.orElse(getMyDefault());
assertEquals("Default Value", defaultText);
}
@Test
public void whenOrElseGetAndOrElseDiff() {
String text = "배부른코딩로그";
System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text)
.orElseGet(this::getMyDefault);
assertEquals("TEST", defaultText);
System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text)
.orElse(getMyDefault());
assertEquals("TEST", defaultText);
}
Optional의 유용한 예제
If 처리 로직을 Optional과 stream 메서드로 간결하게 대체 작성한 예제이다.
public class Modem {
private Double price;
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Modem(Double price) {
this.price = price;
}
}
public boolean priceIsInRange1(Modem modem) {
boolean isInRange = false;
if (modem != null && modem.getPrice() != null && (modem.getPrice() >= 10 && modem.getPrice() <= 15)) {
isInRange = true;
}
return isInRange;
}
@Test
public void whenFiltersWithoutOptional() {
assertTrue(priceIsInRange1(new Modem(10.0)));
assertFalse(priceIsInRange1(new Modem(9.9)));
assertFalse(priceIsInRange1(new Modem(null)));
assertFalse(priceIsInRange1(new Modem(15.5)));
assertFalse(priceIsInRange1(null));
}
public boolean priceIsInRange2(Modem modem) {
return Optional.ofNullable(modem)
.map(Modem::getPrice)
.filter(p -> p >= 10)
.filter(p -> p <= 15)
.isPresent();
}
@Test
public void whenFiltersWithoutOptional2() {
assertTrue(priceIsInRange2(new Modem(10.0)));
assertFalse(priceIsInRange2(new Modem(9.9)));
assertFalse(priceIsInRange2(new Modem(null)));
assertFalse(priceIsInRange2(new Modem(15.5)));
assertFalse(priceIsInRange2(null));
}
Optional과 stream 메서드를 이용한 또 다른 예제다.
@Test
public void givenOptional_whenMapWorks() {
List<String> companyNames = Arrays.asList( "Samsung", "SK", "NAVER", "Daum");
Optional<List<String>> listOptional = Optional.of(companyNames);
int size = listOptional.map(List::size).orElse(0);
assertEquals(4, size);
}
@Test
public void givenOptional_whenMapWorks2() {
String name = "saelobi";
Optional<String> nameOptional = Optional.ofNullable(name);
int len = nameOptional.map(String::length).orElse(0);
assertEquals(7, len);
}
@Test
public void givenOptional_whenMapWorksWithFilter() {
String password = " password ";
Optional<String> passOpt = Optional.of(password);
boolean correctPassword = passOpt.filter( pass -> pass.equals("password")).isPresent();
assertFalse(correctPassword);
correctPassword = passOpt .map(String::trim) .filter(pass -> pass.equals("password")) .isPresent();
assertTrue(correctPassword);
}
출처: https://engkimbs.tistory.com/646 [새로비]
'Java' 카테고리의 다른 글
[Java] Null Safe한 자바 컬렉션 정렬 (0) | 2022.05.24 |
---|---|
[Java] Enum 소화하기 (0) | 2022.05.09 |
[Java] YAML 활용하기 (0) | 2022.05.04 |
[Java] @Required 직접 만들어보기 (0) | 2022.04.23 |
[Java] Array, List, Set, Map 선언과 초기화 (1) | 2021.11.05 |
블로그의 정보
배부른코딩로그
배부른코딩로그