[Java] Java Optional (자바 옵셔널) 정리, 예제모음
| Java Optional( 자바 옵셔널 )
Java Optional 클래스는 Java 8에서 추가되었으며 자바의 고질적인 문제인 NullpointerException 문제를 해결할 수 있는 방법을 제공합니다.
import java.util.Optional;
| of, ofNullable로 객체 감싸기
자바에서 제공하는 객체를 Optional 객체로 감싸기 위해서는 Optional 에서 제공하는 of 와 ofNullable 매서드를 사용합니다. 둘의 차이점은 of는 인자로서 null값을 받지 않는다는 것이고 ofNullable은 null값을 허용한다는 것입니다.
@Test
public void givenNonNull_whenCreatesNonNullable() {
String name = "saelobi";
Optional<String> opt = Optional.of(name);
assertEquals("Optional[saelobi]", opt.toString());
}
아래 코드를 보시면 null값을 of 메서드의 입력으로 받을 시 NullPointerException을 일으킵니다.
@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate() {
String name = null;
Optional<String> opt = Optional.of(name);
}
ofNullable은 일반 객체뿐만 아니라 null값까지 입력으로 받을 수 있다는 것을 아래 코드로 확인해 볼 수 있습니다.
@Test
public void givenNonNull_whenCreatesNullable() {
String name = "saelobi";
Optional<String> opt = Optional.ofNullable(name);
assertEquals("Optional[saelobi]", opt.toString());
}
isPresent 메서드로 현재 Optional이 보유한 값이 null인지 아닌지를 확인할 수 있습니다.
@Test
public void givenOptional_whenIsPresentWorks() {
Optional<String> opt = Optional.of("saelobi");
assertTrue(opt.isPresent());
opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());
}
이 Optional 메서드를 이용하면 다음과 같은 if를 이용한 null값 체크를 대체할 수 있습니다. if를 이용한 null값 체크가 좋지 않은 이유는 크게 2가지가 있습니다.
1. 코드가 길어짐에 따라 코드의 가독성이 점점 떨어지게 된다
2. 각 변수마다 null값을 체크해야 되기 때문에 프로그래머의 실수를 유발할 가능성이 높아진다.
Optional 방식은 위의 문제를 해결하여 가독성 좋고 강건한 코드를 만드는 데 도움을 줍니다. 다음 예제를 보면 어떤 의미인지 바로 아실 수 있을겁니다.
if(name != null){
System.out.println(name.length);
}
위의 if 의 null 체크 방식을 다음과 같이 ifPresent로 간결하게 해결할 수 있습니다.
@Test
public void givenOptional_whenIfPresentWorks() {
Optional<String> opt = Optional.of("saelobi");
opt.ifPresent(name -> System.out.println(name.length()));
}
| orElse, orElseGet으로 Optional 값 가져오기
if에서 null값이 아닌 경우의 처리를 else 키워드 이하의 코드로 해결하지만 Optional 에서는 orElse로 간단하게 해결할 수 있습니다.
@Test
public void whenOrElseWorks() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("John");
assertEquals("John", name);
}
Optional에서는 값을 가져올 때 자주 사용되는 메서드 두 가지가 있습니다. orElseGet, orElse 이 두 가지 입니다. 이 메서드가 자주 사용되는 이유는 null값 체크를 할 수 있음과 동시에 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 = "TEST";
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);
}
또한 get과 orElseThrow를 이용해서 Optional의 값을 얻을 수 있습니다. 하지만 이런 방식은 기존의 null을 체크하는 방식과 다른게 없거니와 오히려 Optional을 써서 타이핑만 더 치는 안 좋은 방법이라고 생각합니다. 하루 빨리 deprecated 됬으면 하는 개인적인 소망입니다.
@Test(expected = IllegalArgumentException.class)
public void whenOrElseWorks1() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow(IllegalArgumentException::new);
}
@Test
public void givenOptional_whenGetsValue() {
Optional<String> opt = Optional.of("saelobi");
String name = opt.get();
assertEquals("saelobi", name);
}
@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException() {
Optional<String> opt = Optional.ofNullable(null);
String name = opt.get();
}
다음은 Optional과 stream 메서드를 이용한 예제입니다. if문으로 처리할 로직을 Optional과 stream 메서드로 간결한 코드로 작성할 수 있거니와 null 체크도 간편하게 할 수 있다는 큰 장점이 있죠.
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://www.baeldung.com/java-optional
출처: https://engkimbs.tistory.com/646?category=790524 [새로비]
'JAVA > Java' 카테고리의 다른 글
java, optional의 orElse와 orElseGet의 차이 (0) | 2021.05.24 |
---|---|
JAVA GC (Garbage Collector) (0) | 2021.04.11 |
자바에서 final에 대한 이해 (0) | 2021.04.07 |
[자바 프로그래밍] 자바 this를 파헤쳐보자 (0) | 2021.03.29 |
[Java] Java 클래스 로딩 과정(Java Class Loading Process) (0) | 2021.03.29 |
Enum 찾기의 달인 (효율적으로 찾기, spring bean과 맵핑) (0) | 2021.03.21 |
lambda (람다, 표현식, 함수형 인터페이스, default 메소드, 메소드 레퍼런스) (0) | 2021.03.21 |
객체지향 설계 5원칙 SOLID (SRP, OCP, LSP, ISP, DIP) (0) | 2021.03.21 |