Optional<T>

Drumj·2023년 2월 24일
0

오늘의 학습

오늘은 스트림을 공부중에 나온 Optional<T>에 대해 알아보자!
이 녀석을 공부하고 이 후에 최종 연산을 배워보도록 하자


Optional<T>

T 타입 객체의 래퍼 클래스 이다.

public final class Optional<T>{
	private final T value; //T 타입의 참조변수
    ...(생략)...
}

모든 종류의 객체를 저장할 수 있다. 그게 null 이 될 수도 있다고 한다.
이게 왜 필요한가??

  1. null을 직접 다루는건 위험하다. NullPointerException이 터질 수 도 있기 때문이다.
  2. null체크를 하려면 if문이 필수이다. 그러면 코드가 지저분 해진다.

이런 문제들을 해결하려고 Optional<T>를 이용해서 간접적으로 null을 다루는 것이다.


Optional<T> 객체 생성

  • Optional객체를 생성할 때는 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()


Optional<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, OptionalLong, OptionalDouble

기본형 값을 감싸는 래퍼클래스 들이다.
우선 OptionalInt를 기준으로 알아보자!

public final class OptionalInt {
	private final boolean isPresent; // 값이 저장되어 있다면 true
    private final int value; //int 타입의 변수
}

값은 어떻게 가져올까?

Optional 클래스값을 반환하는 메서드
Optional<T>T get()
OptionalIntint getAsInt()
OptionalLonglong getAsLong()
OptionalDoubledouble 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은 이제 안녕...!!!

0개의 댓글