개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › Java › 자주 쓰는 클래스

Optional

값이 있는데도 orElse의 기본값 코드는 왜 실행되나

Optional은 값이 있을 수도 없을 수도 있다는 것을 나타내는 상자입니다. 만드는 방법(of, ofNullable, empty)과 꺼내는 방법(get, orElse, orElseGet, map)에 따라, 값이 없을 때 예외가 나기도 하고 기본값이 쓰이기도 합니다. orElse와 orElseGet은 결과가 같아 보여도 기본값을 만드는 코드가 실행되는 때가 다릅니다.

실행으로 대조 · JDK 21에서 스물네 가지 조합을 실행해 출력과 예외를 대조

이 실습에 나오는 개념

Optional
값이 있을 수도 없을 수도 있음을 타입으로 표현하는 컨테이너 클래스다. 메서드가 null을 반환하는 대신 Optional을 반환하면 호출하는 쪽이 값이 없는 경우를 처리하도록 유도할 수 있다. 주로 메서드의 반환 타입으로 사용한다.
orElse와 orElseGet
Optional이 비어 있을 때 대신 쓸 기본값을 정하는 두 메서드다. orElse(값)은 기본값 자체를 인자로 받고, orElseGet(함수)는 기본값을 만들어 내는 함수(람다)를 받는다. 자바는 메서드를 호출하기 전에 인자를 먼저 계산하므로, orElse(makeDefault())라고 쓰면 Optional에 값이 들어 있어도 makeDefault()가 실행된다. orElseGet은 비어 있을 때에만 넘겨받은 함수를 실행한다. 그래서 기본값을 만드는 데 시간이 걸리거나 저장, 기록 같은 다른 일이 함께 일어난다면 orElseGet을 쓴다.
NullPointerException
null인 참조로 메서드를 부르거나 필드를 읽으려고 할 때 발생하는 예외다. null은 가리키는 객체가 없다는 뜻이므로, 없는 객체에게 일을 시킬 수 없어서 예외가 난다. null인지 아닌지는 실행 중의 값에 달려 있어서 컴파일러가 미리 잡아 주지 못하고, 실행해 봐야 드러난다(Unchecked 예외라 처리를 강제하지도 않는다). null을 받지 않도록 만든 메서드(Optional.of, Objects.requireNonNull 등)에 null을 넘겨도 이 예외가 발생한다. 값이 없을 수 있는 곳에서는 먼저 null인지 검사하거나, Optional로 값이 없을 수 있음을 타입에 드러내어 줄인다.
지연 평가(Lazy Evaluation)
결과가 실제로 필요해질 때까지 계산을 미루는 방식이다. 스트림의 중간 연산은 호출 즉시 실행되지 않고 최종 연산이 호출될 때 비로소 실행되고 원소가 하나씩 파이프라인을 지나가며, 필요한 만큼만 처리하고 멈출 수 있다. JPA의 지연 로딩도 같은 발상으로, 연관 데이터를 실제로 사용할 때 조회한다.
람다 표현식
이름 없는 함수를 간결하게 표현하는 문법으로 Java 8에서 도입되었다. (매개변수) -> 실행문 형태로 쓰며, 추상 메서드가 하나뿐인 함수형 인터페이스의 구현으로 사용된다. 동작(코드)을 값처럼 다른 메서드에 전달할 수 있게 해 준다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. String name = "kim": name이 문자열 "kim"을 가리킵니다.
  2. Optional.ofNullable(name): name이 null이 아니므로 값 "kim"을 담은 Optional을 만듭니다.
  3. System.out.println(opt): Optional[kim] 한 줄이 출력됩니다. 대괄호 안이 들어 있는 값입니다.
  4. makeDefault(): orElse를 호출하기 전에 괄호 안의 인자부터 계산합니다. 자바는 메서드를 부르기 전에 넘길 값을 먼저 구하기 때문입니다. opt에 값이 있는지와 상관없이 makeDefault()가 실행되어 "makeDefault 실행"이 출력되고, "손님"이 만들어집니다.
  5. opt.orElse("손님"): 값이 들어 있으므로 "kim"을 돌려줍니다. 방금 만든 "손님"은 쓰이지 않고 버려집니다.
  6. System.out.println(result): kim 한 줄이 출력됩니다.

실행 결과

코드

import java.util.Optional;

public class Main {
    static String makeDefault() {                // 기본값을 만드는 메서드
        System.out.println("makeDefault 실행");
        return "손님";
    }

    public static void main(String[] args) {
        String name = "kim";
        Optional<String> opt = Optional.ofNullable(name);
        System.out.println(opt);

        String result = opt.orElse(makeDefault());
        System.out.println(result);
    }
}

이 모형의 전제

스물네 가지 조합(Optional.empty()는 name을 쓰지 않으므로 서로 다른 경우는 스무 가지)에서 화면에 찍히는 줄과 예외를 이 PC의 JDK 21에서 같은 코드를 실행한 결과와 대조했습니다. makeDefault()가 실행됐는지는 그 메서드가 찍는 줄이 출력에 있는지로 확인했습니다. map이 비어 있을 때 함수를 실행하지 않는다는 것은 Optional의 문서에 적힌 동작이며, 실행에서는 예외 없이 "이름 없음"이 나오는 것으로 확인했습니다. 예외 메시지 "No value present"는 JDK 21 기준입니다. Optional.of(null)의 NullPointerException에는 메시지가 없습니다.

앞 실습: 날짜와 시간 · 다음 실습: 예외와 finally

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요