📖
Aaron's TECH BOOK
  • Intro
    • About me
  • Lecture
    • Kubernetes
      • Begin Kubernetes
    • Kafka
      • Begin Kafka
    • Kotlin
      • TDD, Clean Code Preview
      • woowa Kotlin
    • Java
      • Multithread Concurrency
      • The Java
    • Toby
      • Toby Spring 6
      • Toby Spring Boot
    • MSA
      • 01.Micro Service
      • 02.DDD 설계
      • 03.DDD 구현
      • 04.EDA 구현
    • Spring Boot
    • Spring Batch
    • Spring Core Advanced
    • Spring DB Part II
    • Spring DB Part I
    • JPA API and Performance Optimization
    • JPA Web Application
    • JPA Programming Basic
    • Spring MVC Part 2
      • 01.Thymeleaf
      • 02.ETC
      • 03.Validation
      • 04.Login
      • 05.Exception
    • Spring MVC Part 1
      • 01.Servlet
      • 02.MVC
    • Http
      • 01.Basic
      • 02.Method
      • 03.Header
    • Spring Core
    • Study
      • Concurrency issues
      • First Come First Served
      • Performance Test
      • TDD
      • IntelliJ
  • Book
    • Kafka Streams in Action
      • 01.카프카 스트림즈
      • 02.카프카 스트림즈 개발
      • 03.카프카 스트림즈 관리
    • Effective Kotlin
      • 01.좋은 코드
      • 02.코드 설계
      • 03.효율성
    • 이벤트 소싱과 MSA
      • 01.도메인 주도 설계
      • 02.객체지향 설계 원칙
      • 03-04.이벤트 소싱
      • 05.마이크로서비스 협업
      • 06.결과적 일관성
      • 07.CQRS
      • 08.UI
      • 09.클라우드 환경
    • 몽고DB 완벽 가이드
      • I. 몽고DB 시작
      • II. 몽고DB 개발
    • Kotlin Cookbook
      • 코틀린 기초
      • 코틀린 기능
      • ETC
    • Kotlin in Action
      • 함수/클래스/객체/인터페이스
      • 람다와 타입
      • 오버로딩과 고차 함수
      • 제네릭스, 애노테이션, 리플렉션
    • Kent Beck Tidy First?
    • 대규모 시스템 설계 기초
      • 01.사용자 수에 따른 규모 확장성
      • 02.개략적인 규모 추정
      • 03.시스템 설계 공략법
      • 04.처리율 제한 장치 설계
      • 05.안정 해시 설계
      • 06.키-값 저장소 설계
      • 07.유일 ID 생성기 설계
      • 08.URL 단축기 설계
      • 09.웹 크롤러 설계
      • 10.알림 시스템 설계
      • 11.뉴스 피드 시스템 설계
      • 12.채팅 시스템 설계
      • 13.검색어 자동완성 시스템
      • 14.유튜브 설계
      • 15.구글 드라이브 설계
      • 16.배움은 계속된다
    • 실용주의 프로그래머📖
    • GoF Design Patterns
    • 도메인 주도 개발 시작하기
      • 01.도메인 모델 시작하기
      • 02.아키텍처 개요
      • 03.애그리거트
      • 04.리포지터리와 모델 구현
      • 05.Spring Data JPA를 이용한 조회 기능
      • 06.응용 서비스와 표현 영역
      • 07.도메인 서비스
      • 08.애그리거트 트랜잭션 관리
      • 09.도메인 모델과 바운디드 컨텍스트
      • 10.이벤트
      • 11.CQRS
    • Effective Java 3/E
      • 객체, 공통 메서드
      • 클래스, 인터페이스, 제네릭
    • 소프트웨어 장인
    • 함께 자라기
    • Modern Java In Action
      • 01.기초
      • 02.함수형 데이터 처리
      • 03.스트림과 람다를 이용한 효과적 프로그래밍
      • 04.매일 자바와 함께
    • Refactoring
      • 01.리펙터링 첫 번째 예시
      • 02.리펙터링 원칙
      • 03.코드에서 나는 악취
      • 06.기본적인 리펙터링
      • 07.캡슐화
      • 08.기능 이동
      • 09.데이터 조직화
      • 10.조건부 로직 간소화
      • 11.API 리팩터링
      • 12.상속 다루기
    • 객체지향의 사실과 오해
      • 01.협력하는 객체들의 공동체
      • 02.이상한 나라의 객체
      • 03.타입과 추상화
      • 04.역할, 책임, 협력
      • 05.책임과 메시지
      • 06.객체 지도
      • 07.함께 모으기
      • 부록.추상화 기법
    • Clean Code
    • 자바 ORM 표준 JPA 프로그래밍
Powered by GitBook
On this page
  • Optional Class
  • Optional 적용 패턴
  • Optional 활용
  • Date & Time API
  • java.time
  1. Book
  2. Modern Java In Action

04.매일 자바와 함께

Optional Class

Optional 형식을 통해 도메인 모델의 의미를 명확히 만들고, null 참조 대신 값이 없는 상황을 표현해 보자.

Null 참조의 문제점

  • 에러의 근원 : NullPointerException

  • 코드를 어지럽힘 : null 확인 코드

  • 아무 의미가 없음 : null 은 아무 의미도 표현하지 않는다.

  • 자바 철학에 위배 : 자바는 개발자로부터 모든 포인터를 숨겼지만 null 포인터는 예외

  • 형식 시스템에 구멍을 만듦 : null의 의미를 알 수 없음

java.util.Optional<T>

  • 값이 있을 경우 Optional 클래스는 값을 감싼다.

  • 값이 없으면 Optional.empty

Optional 적용 패턴

Optional 객체 만들기

  • 빈 Optional

    Optional<Car> optCar = Optional.empty();
  • null이 아닌 Optional

    Optional<Car> optCar = Optional.of(car);
  • null 값으로 Optional 만들기

    Optional<Car> optCar = Optional.ofNullable(car);

Map으로 Optional 값을 추출하고 변환하기

Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

flatMap으로 Optional 객체 연결

Optional<Person> optPerson = Optional.of(person);
Optional<String> name = optPerson.flatMap(Person::getCar)
    							.flatMap(Car::getInsurance)
							    .map(Insurance::getName)
    							.orElse("Unkown");

Optional의 직렬화 불가

  • Optional은 Serializable Interface를 구현하지 않는다.

  • Optional 클래스를 필드 형식으로 사용할 수 없으니, Optional 로 값을 반환받을 수 있는 메서드를 추가하자.

    public class Person {
        private Car car;
        public Optional<Car> getCarAsOptional() {
            return Optional.ofNullable(car);
        }
    }

Optional 스트림 조작

public Set<String> getCarInsuranceNames(List<Person> persons) {
    Stream<Optional<String>> stream =  persons.stream()
        .map(Person::getCar) //return Stream<Optional<Car>>
        .map(optCar -> optCar.flatMap(Car::getInsurance)) //return Optional<Insurance>
        .map(optInsurance -> optInsurance.map(Insurance::getName)) //return Optional<String> mapping
        .flatMap(Optional::stream) //return Stream<Optional<String>>
        .collect(toSet());
    
    return stream.filter(Optional::isPresent) //null이 아닌 값만 전달
        		.map(Optional::get)
        		.collect(toSet());
}

Default Action & Optional unwrap

  • get() : Optional 에 값이 반드시 있을 경우 사용하자. (없을 경우 NoSuchElementException 발생)

  • orElse(T other) : Optional이 값을 포함하지 않을 때 기본값 제공

  • orElseGet(Supplier<? extends T> other) : Optional 이 비어있을 경우 기본값 생성

  • orElseThrow(Supplier<? extends X> exceptionSupplier) : Optional이 비어있을 때 예외 발생

  • ifPresent(Comsumer<? super T> consumer) : 값이 존재할 경우 인수로 넘겨준 동작 실행

  • ifPresentOrElse(Comsumer<? super T> action, Runnable emptyAction) : Optional 이 비었을 때 실행할 수 있는 Runnable을 인수로 받음

두 Optional 합치기

  • Before

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
    if (person.isPresent() && car.isPresent()) {
        return Optional.of(findCheapestInsurance(person.get(), car.get()));
    } else {
        return Optional.empty();
    }
}
  • After

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
    return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}

필터로 특정 값 거르기

  • Optional 에 값이 있을 경우 filter 동작

Optional<Insurance> optInsurance = Optional.of(insurance);
optInsurance.filter(insurance ->
                   "CambridgeInsurance".equals(insurance.getName()))
    			.ifPresent(x -> System.out.pringln("ok"));
int minAge = 20;
Optional<Person> optPerson = Optional.of(person);
//Person이 minAge 이상의 나이일 경우에만 보험회사 이름 반환
Optional<String> name = optPerson.filter(p -> p.getAge() >= minAge)
							    .flatMap(Person::getCar)
    							.flatMap(Car::getInsurance)
							    .map(Insurance::getName)
    							.orElse("Unkown");

Reference

Optional 활용

잠재적으로 null이 될 수 있는 대상을 Optional로 감싸기

Optional<Object> value = Optional.ofNullable(map.get("key"));

예외와 Optional 클래스

  • 예외를 빈 Optional로 처리하기

    //OptionalUtility.java
    public static Optional<Integer> stringToInt(String s) {
        try {
            return Optional.of(Integer.parseInt(s));
        } catch {
            return Optional.empty();
        }
    }

기본형 Optional 을 사용하지 말자

  • 기본형 Optional 에는 OptionalInt, OptionalLong, OptionalDouble 등이 있다.

    • 이 기본형 특화 Optional은 다른 일반 Optional과 혼용할 수 없다.

응용

  • Optional로 프로퍼티에서 지속 시간 읽기

    public int readDuration(Properties props, String name) {
        return Optional.ofNullable(props.getProperty(name)) //null일 경우 Optional 처리
            			.flatMap(OptionalUtility::stringToInt) //OptionalUtility.stringToInt 메서드 참조
            			.filter(i -> i > 0) //음수 필터링
            			.orElse(0); //기본값 0
    }

Date & Time API

java.time

LocalDate

  • 시간을 제외한 날짜를 표현하는 불변 객체

  • 생성

    LocalDate date = LocalDate.of(2022, 1, 1);
    
    //현재 날짜 정보
    LocalDate today = LocalDate.now();
    
    //parse 정적 메서드 사용
    LocalDate date = LocalDate.parse("2022-01-01");
  • 사용

    int year = date.getYear(); // 2022
    int monthValue = date.getMonthValue(); // 1
    Month month = date.getMonth(); // JANUARY
    int day = date.getDayOfMonth(); // 1
    DayOfWeek dow = date.getDayOfWeek(); // SATURDAY
    int len = date.lengthOfMonth(); // 31 (days in JANUARY)
    boolean leap = date.isLeapYear(); // false (not a leap year), 윤년 여부
    System.out.println(date); //2022-01-01
    
    //TemporalField를 이용한 LocalDate 값 읽기
    int year = date.get(ChronoField.YEAR); // 2022
    int month = date.get(ChronoField.MONTH_OF_YEAR); // 1
    int day = date.get(ChronoField.DAY_OF_MONTH); // 1

LocalTime

  • 날짜를 제외한 시간을 표현하는 불변 객체

  • 생성

    LocalTime time = LocalTime.of(12, 34, 56); // 12:34:56
    
    //parse 정적 메서드 사용
    LocalTime time = LocalTime.parse("12:34:56");
  • 사용

    int hour = time.getHour(); // 12
    int minute = time.getMinute(); // 34
    int second = time.getSecond(); // 56

LocalDateTime

  • 날짜와 시간을 모두 표현

  • 생성

    //2022-01-01T12:34:56
    LocalDateTime dt1 = LocalDateTime.of(2022, Month.JANUARY, 1, 12, 34, 56);
    
    // LocalDate + LocalTime
    LocalDateTime dt2 = LocalDateTime.of(date, time);
    
    // LocalDate <- atTime
    LocalDateTime dt3 = date.atTime(12, 34, 56);
    
    // LocalDate <- LocalTime
    LocalDateTime dt4 = date.atTime(time);
    
    // LocalTime <- LocalDate
    LocalDateTime dt5 = time.atDate(date);
  • 사용

    LocalDate date1 = dt1.toLocalDate();
    LocalTime time1 = dt1.toLocalTime();

Instant

  • 기계 전용 유틸리티

  • Unix epoch time 기준으로 특정 지점까지의 시간을 초로 표현

  • 나노초(10억분의 1초)의 정밀도 제공

    Instant.ofEpochSecond(3);
    Instant.ofEpochSecond(3, 0);
    Instant.ofEpochSecond(2, 1_000_000_000); //1초 후의 나노초
    Instant.ofEpochSecond(4, -1_000_000_000); //4초 전의 나노초

Duration

  • Duration d1 = Duration.between(time1, time2);
    Duration d2 = Duration.between(dateTime1, dateTime2);
    Duration d3 = Duration.between(instant1, instant2);
    
    //시간 객체를 사용하지 않고 생성
    Duration threeMinutes = Duration.ofMinutes(3);
    Duration threeMinutes = Duration.ofMinutes(3, ChronoUnit.MINUTES);

Period

  • Period tenDays = Period.between(LocalDate.of(2022, 1, 1),
                                   LocalDate.of(2022, 1, 11));
    
    //시간 객체를 사용하지 않고 생성
    Period tenDays = Period.ofDays(10);
    Period threeWeeks = Period.ofWeeks(3);
    Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

간격을 표현하는 날짜와 시간 클래스의 공통 메서드

- between
- from
- of
- parse
- addTo
- get
- isNegative
- isZero
- minus
- multipliedBy
- negated
- plus
- subtractFrom

Last updated 1 year ago

는 LocalDate, LocalTime, LocalDateTime, Instant, Duration, Period 등 새로운 클래스를 제공

두 시간 객체 사이의 지속시간

두 시간 객체 사이의 지속 시간을 년,월,일로 표현할 경우

Optional Class Method
java.time package
Docs
Docs