[Java] Java Optional (자바 옵셔널) 정리, 예제모음
- Language & Solution/Java
- 2018. 11. 21. 15:01
| 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()); }
@Test public void givenNull_whenCreatesNullable() { String name = null; Optional<String> opt = Optional.ofNullable(name); assertEquals("Optional.empty", 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
'Language & Solution > Java ' 카테고리의 다른 글
[Java Library] slf4j + log4j2 한 방 정리 (0) | 2019.05.27 |
---|---|
[Java] 새로운 게 무조건 더 좋은 방법은 아니다: Collections.nCopies()와 Stream에 관하여 (0) | 2019.03.08 |
[Java] Java 클래스 로딩 과정(Java Class Loading Process) (0) | 2018.11.12 |
[Java Library] Executor Framework (0) | 2018.09.25 |
[Java] JDK 소스코드 붙이기 (0) | 2018.06.24 |
이 글을 공유하기