오늘은 스트림을 공부중에 나온 Optional<T>
에 대해 알아보자!
이 녀석을 공부하고 이 후에 최종 연산
을 배워보도록 하자
<T>
T 타입 객체의 래퍼 클래스 이다.
public final class Optional<T>{
private final T value; //T 타입의 참조변수
...(생략)...
}
모든 종류의 객체를 저장할 수 있다. 그게 null
이 될 수도 있다고 한다.
이게 왜 필요한가??
null
을 직접 다루는건 위험하다.NullPointerException
이 터질 수 도 있기 때문이다.null
체크를 하려면if
문이 필수이다. 그러면 코드가 지저분 해진다.
이런 문제들을 해결하려고 Optional<T>
를 이용해서 간접적으로 null
을 다루는 것이다.
<T>
객체 생성of()
나 ofNullable()
을 사용한다.String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(new String("abc"));
Optional<String> optVal = Optional.of(null); //NullPointerException 발생
Optional<String> optVal = Optional.ofNullable(null); //OK
위 코드에서 보듯이 of()
의 값이 그냥 null
이면 NullPointerException
을 발생시킨다
그래서 값이 null
일 가능성이 있다면 ofNullable()
을 사용하는게 좋다.
null
대신 빈 Optional<T>
객체를 사용하자.Optional<T>
타입의 참조변수를 기본값으로 초기화 할 때는 empty()
를 사용하는게 바람직 하다. null
을 사용할 수도 있지만 권장하지 않는다.Optional<String> optVal = null; // 널로 초기화, 바람직 하지 않음.
Optional<String> optVal = Optional.<String>empty() ; // 빈 객체로 초기화.
<String>
은 생략할 수 있다고 한다 -> Optional.empty()
<T>
객체의 값 가져오기값을 가져올 때는 get(), orElse(), orElseGet(), orElseThrow()
를 사용한다.
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환, null이면 예외발생
String str2 = optVal.orElse(""); // 저장된 값이 null이면, ""을 반환
String str3 = optVal.orElseGet(Sting::new); // 람다식 사용가능
String str4 = optVal.orElseThrow(NullPointerException::new); // 널이면 지정된 예외 발생
여기서 orElse(), orElseGet()
를 가장 많이 사용한다고 한다.
T orElseGet(Supplier<? extends T> other)
T orElseThrow(Supplier<? extends X> exceptionSupplier)
orElseGet(), orElseThrow()
는 위와 같이 정의되어 있다.
orElseGet()
: null을 대체할 값을 람다식으로 지정할 수 있다
orElseThrow()
: null일 때 지정된 예외를 발생시킬 수 있다.
isPresent()
와 ifPresent()
isPresent()
: Optional 객체의 값이 null 이면 false 를 아니면 true를 반환ifPresent()
: 값이 있으면 주어진 람다식을 실행하고, 없으면 아무일도 발생하지 않는다.코드를 통해 알아보자
ifPresent(Consumer<T> block) // 이렇게 정의 되어있다.
//1번 if 사용
if(str !=null) {
System.out.println(str);
}
//2번 isPresent() 사용
if(Optional.ofNullable(str).isPresent()) {
System.out.println(str);
}
//3번 ifPresent() 사용
Optional.ofNullable(str).ifPresent(System.out.println);
순차적으로 코드가 단순해지는 걸 알 수 있다!
그렇다면 간단하게 Optional<T>
에 대한 예제를 살펴보자!
//Optional<T>
Optional<String> opt = null;
System.out.println("opt = " + opt);
//System.out.println("opt = " + opt.get()); //NullPointerException 발생
Optional<String> opt2 = Optional.empty();
System.out.println("opt2 = " + opt2);
//System.out.println("opt2 = " + opt2.get()); //NoSuchElementException 발생
Optional<String> opt3 = Optional.of("ABC");
System.out.println("opt3.get() = " + opt3.get());
String str = "";
try {
str = opt2.get(); //NoSuchElementException 발생 -> try-catch 해줘야됨.
} catch (Exception e) {
str ="예외 발생!"; // 예외가 발생하면 이걸로 대체
}
System.out.println("str.get() = " + str);
str = opt2.orElse("예외 발생! orElse()");
System.out.println("str.ofElse() = " + str);
str = opt2.orElseGet(() -> new String("예외 발생! orElseGet()"));
System.out.println("str.orElseGet() = " + str);
실행 결과는 위와 같다.
기본형 값을 감싸는 래퍼클래스 들이다.
우선 OptionalInt
를 기준으로 알아보자!
public final class OptionalInt {
private final boolean isPresent; // 값이 저장되어 있다면 true
private final int value; //int 타입의 변수
}
값은 어떻게 가져올까?
Optional 클래스 | 값을 반환하는 메서드 |
---|---|
Optional<T> | T get() |
OptionalInt | int getAsInt() |
OptionalLong | long getAsLong() |
OptionalDouble | double getAsDouble() |
메서드 이름이 조금씩 다르다는 것만 주의하자!
자 여기서 int의 기본값은 0이라는 것을 알고 있을 것이다. 그렇다면 0을 넣어서 생성한 것과 empty()
를 사용해서 기본값 0을 넣은 것은 어떻게 다르지...?? 이거... 구분 가능하나????라고 생각할 수 있다!!
위에 정의된 것을 보면 isPresent
가 있을 것이다. 이 녀석 덕분에 구분이 가능 하다고 한다.
OptionalInt optInt = OptionalInt.of(0); //0을 저장
OptionalInt optInt2 = OptionalInt.empty(); //비어있어서 알아서 0을 저장
//비교해보기
System.out.println(optInt.isPresent()); //true
System.out.println(optInt2.isPresent()); //false
System.out.println(optInt.equals(optInt2)); //false
이렇게 또 Optional<T>
에 대해 알아봤다
여기서 가장 중요한 것은 이녀석을 사용하는 이유이다.
null을 간접적으로 다뤄서 코드의 간결함과 안정성을 높이기 위해서이다.
정말정말 싫은 NullPointerException
은 이제 안녕...!!!