simDev1234
심플하고 차분하게
simDev1234
전체 방문자
오늘
어제
  • 분류 전체보기
    • Computer Science
      • Basic Math
      • Data Structure
      • Algorithm
      • Database
      • OS
    • Language
      • Java
      • Kotlin
      • SQL
    • Framework
      • Spring
      • Orm&Mapper
      • 프로젝트로 스프링 이해하기
      • 스프링 라이브러리
    • Infra
      • Cloud
      • Docker
      • Redis
      • AWS, Azure
      • Device
    • Etc
      • CleanCoding
    • Git,Github

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • scanner #next() #nextLine()
  • 자바메모리구조
  • null
  • 자바프로그램
  • 참조변수
  • JVM메모리구조
  • 참조타입
  • 404
  • 자바프로그래밍
  • 자바
  • 스프링
  • 컨트롤러
  • controllerTest

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
simDev1234

심플하고 차분하게

Language/Java

JAVA 라이브러리 - Optional<T> 클래스

2022. 9. 15. 21:09

|  Optional<T> 클래스란?

- 자바에서 모든 객체는 Referece Type으로 nullable하다. * null이 발생할 수 있는 타입

  > Referece Type의 객체는 null을 발생시킬 수 있다. -- NullPointerException

- Optional<T>은 객체를 nullable하게 쓸 수 있도록 하는 Wrapper class로, 객체 사용 시 null을 명시적으로 처리하게 한다.

  * 참고로 코틀린의 경우, nullable한 타입을 구분하는 코드가 별도로 존재한다.

Optional은 주로 "결과 없음"을 명확하게 나타내야 하고, 
null을 사용하면 오류가 발생할 수 있는 메소드 반환 유형으로 사용된다. 
Optional 변수는 null일 수 없으며 항상 Optional 인스턴스를 가리켜야한다. -- Oracle Docs

- 장점 : null을 직접 핸들링하지 않음, null 여부를 타입만으로 나타낼 수 있음, 체이닝을 통한 중간 및 종단 처리

  Optional<T> Optional.empty()
  T null
더보기

- Optional의 생성자를 보면, 아래와 같이 Objects.requireNonNull()을 통해

  감싸려는 객체 데이터가 null일 때, NullPointException을 발생시키도록 한다.

- 따라서 Optional로 감싼다고 하여도 NullPointException이 발생하지 않는 것은 아니다.

private Optional() {
    this.value = null;
}

private Optional(T value) {
    this.value = Objects.requireNonNull(value); // null이면 NullPointException 발생
}
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

 

 

 

|  Optional 객체의 맴버 변수와 생성자

private static final Optional<?> EMPTY = new Optional<>();

private final T value;

private Optional() {
    this.value = null;
}

 

|  Optional 객체의 메소드

isPresent(): boolean null인지 아닌지 여부를 boolean으로 반환
of() 데이터를 Optional<T>로 감싼다.
ofNullable() of()와 동일하나 null이 발생할 때 처리 **null 발생 시 꼭 이 메소드 사용
get() null이 아닌 경우, 해당값 반환 **null일 때 예외 발생시키므로, 사용 시 주의요함
orElse(<T>) null일 경우, 특정 데이터를 반환
orElseGet(() -> getMethod()) null일 경우, 함수형 메소드 실행
orElseThrow(()->new Exception()) null일 경우, 지정한 예외 발생
filter() 조건식에 따라 필터링
map() 조건식에 따라 데이터를 매핑
flatMap() map과 동일하나 null처리가 다름

 

(1) of()와 ofNullable()의 차이

- ofNullable()의 경우 null이 발생할 때 empty() 메소드를 사용하는데, empty() 메소드를 쓰면 Empty 맴버 변수를 반환한다.

private static final Optional<?> EMPTY = new Optional<>();

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

(2) isPresent(), ifPresent() 

public boolean isPresent() {
    return value != null;
}

public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

(3) get(), orElse(), orElseGet(), orElseThrow()

- get()은 사용을 지양하는 편이 좋다고 한다.* 감싼 데이터값이 null일 때, NoSuchElementException이 발생

public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

public T orElse(T other) {
    return value != null ? value : other;
}


public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

- orElse()과 orElseGet()의 차이점은 인자로 함수(ex. getRandomNumber())를 전달할 때 

  > orElse()의 경우 null이 발생하지 않아도 함수를 실행한 후 결과값을 orElse(//결과값//)에 전달하는 한 편,

  > orElseGet()을 쓰면 Supplier라고 하는 함수형 인터페이스를 통해

                                    null이 발생할 때에만 함수를 실행한뒤 결과값을 전달한다.

// 가정: optionalUser.findById(userId)는 null X

// getRandomUser() 실행 --> 결과값이 orElse()안에 담김
optionalUser.findById(userId).orElse(getRandomUser());

// getRandomUser() 실행 X
optionalUser.findById(userId).orElseGet(() -> getRandomUser());

- orElseThrow()는 null 발생 시, 지정한 Exception을 반환하게 하는 메소드이다.

(4) filter(), map(), flatMap()

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

 

|  언제쓸까? - 값이 없을 수도 있을 때

(1) DB를 조회할 때

// JPA을 통해 Optional<T> 타입의 반환값이 null일 경우 처리하는 법
Optional<User> user = userRepository.findById(userId)
                                    .orElseThrow(() -> new Exception());

String userName = userRepository.findUserNameById(userId)
				.map(user -> user.getUserName())
				.orElse("이름 없음");

(2) 조회한 객체에 특정 칼럼이 비어있을 때

@Entity(name = "user")
public class User{
    @Id
    @GeneratedValue
    private Long id;
    
    private String userId;
    private String password;
    
    private String thumbnail;
    
    public Optional<String> getThumbnail(){
    	return Optional.ofNuallable(thumbnail);
    }
    
}

(3) 기존 시스템의 리턴 값이 null을 리턴할 때

Optional.ofNuallable(userRepository.findByName(userName))
		.ifPresent(user -> list.add(user));

(4) 필드에 Optional은 지양하기

- Optional은 함수 반환을 목적으로 만들어졌다.

- Serializable을 구현하지 않아 직렬화를 하면 이상한 값이 나온다.

[직렬화란]

메모리는 가상 메모리와 물리 메모리로 나뉘어져 있다.
가상 주소는 논리적인 주소일 뿐이지, 실제 저장되는 위치는 다를 수 있다.
객체를 그대로 저장하게 되면, 가상 주소까지 저장되게 되는데, 메모리 주소는 다른 시스템에 전달해도 의미가 없다.
따라서 불필요한 정보는 제외하고 타입 정보, 값 정보를 Byte형태로 만들어 보내는데 이를 직렬화라 한다.
직렬화를 하려면 클래스가 serializable를 구현해야한다.

- Optional.empty()를 빠뜨릴 확률이 높다. (휴먼 에러)

 

|  Optional을 제대로 쓰기

(1) 비어있을 컬렉션을 표현할 땐 Collections.emptyList()를 사용한다.

Optional<List<User>> findAll();  // !!사용하지 말아야 한다.

Collections.emptyList(); // Collections의 emptyList()를 사용한다.

- 컬렉션 요소에 Optional은 절대 금지

// 사용자가 Optional을 매번 판단해야하는 상황발생
List<Optional<User>> findAll();  // !!사용하지 말아야 한다. 

// ----사용시
for (Optional<User> opt : list) {

    // 매번 optional을 객체로 변환
    User user = opt.get(); // 불필요하다.

}

// User타입 그대로 반환
List<User> findAll();

(2) Optional을 파라미터로 넘기지 않는다.

(3) Optional 값을 가져올 때 .get()은 되도록 지양한다.

- get()을 쓰면, null일 때, NoSuchElementException을 발생시킨다.

- optional의 중단, 종단 메소드를 체이닝하여 사용하는게 낫다.

(4) Optional의 중간, 종단 메소드를 쓰기 위해 불필요한 옵셔널을 하지 않는다.

 

 

[ 참고 및 출처 ]

부트 캠프 수업을 들은 후 정리

https://tecoble.techcourse.co.kr/post/2021-06-20-optional-vs-null/

http://www.tcpschool.com/java/java_stream_optional

orElse()와 orElseGet()의 차이 : https://kdhyo98.tistory.com/40

'Language > Java' 카테고리의 다른 글

[이펙티브 자바] 객체의 생성  (0) 2022.09.29
JAVA 유용한 타입 - Enum  (0) 2022.09.15
SOLID 원칙  (0) 2022.08.29
JAVA 라이브러리 - 컬렉션  (0) 2022.08.11
JAVA 라이브러리 - 제네릭 클래스  (0) 2022.08.10
    'Language/Java' 카테고리의 다른 글
    • [이펙티브 자바] 객체의 생성
    • JAVA 유용한 타입 - Enum
    • SOLID 원칙
    • JAVA 라이브러리 - 컬렉션
    simDev1234
    simDev1234
    TIL용 블로그. * 저작권 이슈가 있는 부분이 있다면 댓글 부탁드립니다.

    티스토리툴바