자바(java) - Optional이란?

2023. 12. 6. 02:40·JAVA-기초/JAVA기본
728x90
반응형
SMALL

Optional이란?

  • Java8에서부터 지원하는 기능
  • java.util의 하위 클래스
  • NPE를 방지할 수 있도록 도와준다.
  • null이 올 수 있는 값을 Wrapper클래스로 감싸준다.

NPE란?

  • NullPointerException
  • 가장 많이 발생하는 예외중 하나
  •  null 여부를 검사함으로써 예외가 터지는것을 방지할 수 있다.
  • 검증하는 코드는 복잡하고 번거롭다.

NPE 예시 코드

@Test
    public void test01() {
        String name = null;
        System.out.println(name.charAt(0)); // name은 null이기 때문에 NPE가 발생한다.

        if (name == null) {
            name = "userA"; //null을 체크해서 값이 없는 경우 새로운 값을 삽입하는 코드
        }
    }

 

NPE 예시 코드 결과 화면

  • System.out.println(name.charAt(0))에서 name의 인덱스를 검색하는 과정에서 값이 없다.
  • 컴파일하는 과정에서 예외 발생 => NPE
  • 아래의 if문을 출력문 위로 올려주면 null이 발생하지 않는다.

 

문제 해결 코드

@Test
public void test01() {
    String name = null;
    if (name == null) {     //null인 name에 값을 삽입
        name = "userA";
    }
    System.out.println(name.charAt(0)); //정상적으로 'u'가 출력된다.

}

 

NPE 해결 코드 결과 화면

  • 정상적으로 name의 0번째 문자가 출력되는 것을 볼 수 있다.

 

위 코드의 문제점

  • 지금은 단순해보이는 코드이지만 나중에 로직이 복잡해질 경우 코드또한 매우 복잡해지고 유지보수하기에 코드가 하눈에 들어오지 않는다.
  • 하나하나 모두 null을 검증해야 하는데 그 때마다 if문을 남발해야한다.
  • 그래서 필요한것이 Optional 클래스!

 

 

 

Optional의 메서드들 (Java8 공식 문서 참조)

 

아래와 같이 여러가지 메서드들을 Optional에서 지원하고 있다. 메서드들을 천천히 하나씩 알아보도록 하자.

 

 

Optional 활용하기

1. Optional 생성

 

   optional을 생성한 후 해당 클래스의 내용을 살펴보면 어떻게 생성되는지 알 수 있다.

- 첫 번째, 내부에서 static 변수로 미리 객체를 생성해놓았기 때문에, 객체를 여러 번 생성하는 경우에도 오직 1개의 EMPTY객체를 공유하여 사용하고 있다. 이는 SINGLETON 패턴으로 메모리를 절약할 수 있는 방법이다.
- 두 번째, value라는 값을 갖고 있으며, Optional 객체를 생성시 value는 null값으로 지정된다.

public final class Optional<T> {
    /**
     * Common instance for {@code empty()}.
     */
    private static final Optional<?> EMPTY = new Optional<>();

    /**
     * If non-null, the value; if null, indicates no value is present
     */
    private final T value;

    /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
    private Optional() {
        this.value = null;
    }

 

- 세 번째, Optional객체는 Wrapper클래스라 값이 없을수도 있는데 empty()메서드를 호출하여 값을 만들 수 있다.

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

 

 

 

2. Optional메서드들 - static 메서드

📌Optional.of(T value)

데이터가 절대 null이 아니라는 확신이 있다면 of()메서드를 사용할 수 있다.

@Test
public void ofMethod(){
    String name = "userA";
    //of는 절대 값이 null이 아닐때 사용한다. Optional.of()로 null을 저정하려고 하면 NPE발생
    Optional<String> op = Optional.of(name);

    boolean result = new String("userA").equals(op.get()); //op에 담긴 값과 새로운 "userA"의 값이 같은지 확인
    System.out.println(result); //true 출력

}

 

📌Optional.ofNullable(T value)

어떤 값이 null이 올 수도 있고 아닐수도 있는 경우에 사용할 수 있다.

그 이후 orElse , orElseGet 메서드를 사용하서 값이 없는 경우에도 NPE발생을 예방할 수 있다.

두 메서드의 차이는 아래와 같다.

  • orElse : 파라미터로 값(원시형)을 받는다.
  • orElseGet : 파라미터로 함수형 인터페이스(참조형)를 받는다.
public T orElse(T other) {
    return value != null ? value : other;
}


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

 

@Test
public void ofNullableMethod(){
    String name = null;

    Optional<String> op = Optional.ofNullable(name);//name은 null일수도있고 아닐수도 있다.
    String result = op.orElse("이 값은 null입니다."); //값이 없다면 이 문장을 출력
    System.out.println("result = " + result);

    String userA = op.orElseGet(() -> new String("userA")); //해당 값이 존재하지 않는다면 새로운 객체를 생성
    System.out.println("userA = " + userA);

}

 

 

더 자세한 orElse와 orElseGet차이 예시

우선 아래 코드와 결과 코드를 확인하자

예시 코드

@Test
public void orElseAndElseGetMethod(){
    String name = "userA";
    String orElseResult = Optional.ofNullable(name).orElse(getName());

    System.out.println("orElseResult = " + orElseResult);

    String orElseGetResult = Optional.ofNullable(name).orElseGet(this::getName);

    System.out.println("orElseGetResult = " + orElseGetResult);
            
            
}

private String getName() {
    System.out.println("getName메서드 호출");
    return "userB";
}

 

결과

  • orElse는 해당 Optional객체의 값이 null이든 아니든 항상 getName메서드를 호출 시킨다.
  • orElseGet은 getName메서드를 null일 경우에만 호출시킨다.
  • 이 차이는 매우 중요하다. orElse를 잘못 사용하면 getName을 호출시키는 순간 잘못된 결과를 초래할 수 있다.

 

 

잘못된 결과를 초래하는 코드(JPA사용)

아래코드에서는 어떤 잘못된 결과를 초래할 수 있을까?

만약 User의 Name테이블이 PK이거나 Unique가 걸려있을 경우 제약조건에 의해 orElse에서 createUserWithName을 호출하는 순간 예외가 발생한다.

또한 orElse는 해당메서드를 호출하므로 자원또한 낭비하게된다.

이러한 코드가 수행되면 서버에 장애가 생길 수 있으므로 orElse가 아닌 orElseGet을 사용해야 한다.

public void findByUserName(String Name) {
    
    return userRepository.findByUserName(Name)
            .orElse(createUserWithName(Name)); //호출되는 순간 new User를 생성해버린다.
}

private String createUserWithName(String Name) {
    User newUser = new User(Name);
    return userRepository.save(newUser);
}

 

orElseGet으로 수정한 코드

public void findByUserName(String Name) {
    
    return userRepository.findByUserName(Name)
            .orElseGet(this::createUserWithName(Name)); null일 경우에만 메서드 호출
}

private String createUserWithName(String Name) {
    User newUser = new User(Name);
    return userRepository.save(newUser);
}

 

 

아래의 글은 망나니개발자님의 블로그를 참고하여 만들었습니다.

https://mangkyu.tistory.com/70

728x90
반응형
SMALL

'JAVA-기초 > JAVA기본' 카테고리의 다른 글

[Java- Algorithm] 너비 우선 탐색법(BFS과 큐(Queue)  (0) 2024.02.28
[자료구조 - Graph 와 DFS] Graph와 DFS란?  (0) 2024.02.24
[Java] 객체지향개념(OOP) 캡슐화와 정보은닉  (3) 2023.09.03
객체지향 프로그래밍 5가지 설계 원칙, SOLID- 단일책임의 원칙  (64) 2023.08.12
자바(Java) - 쓰레드란? start와 run의차이  (0) 2023.04.03
'JAVA-기초/JAVA기본' 카테고리의 다른 글
  • [Java- Algorithm] 너비 우선 탐색법(BFS과 큐(Queue)
  • [자료구조 - Graph 와 DFS] Graph와 DFS란?
  • [Java] 객체지향개념(OOP) 캡슐화와 정보은닉
  • 객체지향 프로그래밍 5가지 설계 원칙, SOLID- 단일책임의 원칙
공부하고 기억하는 공간
공부하고 기억하는 공간
IT 비전공자로 시작하여 훌륭한 개발자가 되기 위해 공부하고 있는 공간입니다. 틀린 내용이나 부족한 부분이 있으면 댓글로 알려주세요 바로 수정하겠습니다.
    250x250
  • 공부하고 기억하는 공간
    IT - railroad
    공부하고 기억하는 공간
  • 전체
    오늘
    어제
    • 분류 전체보기 (313)
      • 면접 준비 (35)
        • OS (6)
        • Spring Security (0)
        • Java (1)
        • DB (9)
        • Network (3)
      • ElasticSearch (2)
      • Kafka (4)
      • Spring (22)
        • Spring Cloud (7)
        • Security6 (5)
        • JPA (12)
        • 프로젝트 리팩토링 회고록 (4)
        • Logging (8)
        • Batch (2)
      • Redis (17)
        • Redis 개념 (8)
        • Redis 채팅 (5)
        • Redis 읽기쓰기 전략 (1)
      • AWS (11)
      • 리눅스 (29)
        • 리눅스 마스터 2급 (5)
        • 네트워크(기초) (7)
        • 리눅스의 이해 (6)
        • 리눅스의 설치 (2)
        • 리눅스 운영 및 관리 (6)
      • JAVA-기초 (16)
        • JAVA기본 (11)
        • Design Pattern (5)
      • JSP (27)
        • JSP 기본 개념 (10)
        • JSP (1)
      • SQL (1)
      • TIL (36)
      • 문제 풀이 (2)
        • Programmers (9)
        • 백준 문제풀이 (28)
      • JavaScript (10)
      • HTML (17)
      • Ngrinder (1)
        • Ngrinder 문서 정리 (1)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      JavaScript
      레디스
      HTML
      자바 면접질문
      리눅스마스터2급정리
      spring redis
      자바 반복문
      자바 알고리즘
      java
      자바
      jsp request
      스프링프레임워크
      redis 채팅
      리눅스
      CSS
      프로그래머스
      자바스크립트
      JSP
      자바 면접
      자바기초
      JS
      Til
      Spring Data Redis
      springsecurity
      리눅스마스터2급
      jsp기초
      Springframework
      Spring
      redis
      백준
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    공부하고 기억하는 공간
    자바(java) - Optional이란?
    상단으로

    티스토리툴바