본문 바로가기
Spring

[Spring] Optional <T>

by happyhelen 2021. 8. 26.

Optional<T> 는 NPE: NullPointerException 을 피하기 위한 클래스이다

Optional<T> 는 null이 될 수 있는 값을 감싸는 Wrapper 클래스로, null 이 오더라도 NPE가 발생하지 않게 해주고, Optional<T>는 클래스이기 때문에 다양한 메소드를 제공한다

 

어떤 데이터가 null이 올 수 있는 경우 해당 값을 Optional<> 로 감싸고, orElse 또는 orElseGet 메소드를 사용해 값이 없는 경우라도 NPE를 발생시키지 않고 값을 가져올 수 있다

// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
// getName() 은 값이 있을 수도, null 일 수도.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("anonymous"); // 값이 없다면 "anonymous" 를 리턴

 


<회원관리 예제에서 Optional<T> 이해하기>

 

package hello.hellospring.repository;

import ..

// 실무에서는 동시성문제때문에 ConcurrentHashMap, AtomicLong 사용

public class MemoryMemberRepository implements MemberRepository{

    // value가 Member타입인 이유: name외에 다른정보가 들어갈 수 있기때문
    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }
    
	// ***
    @Override
    public Optional<Member> findById(Long id) {
        // 결과로 null이 반환될 가능성
        return Optional.ofNullable(store.get(id));

    }
    
	// ***
    @Override
    public Optional<Member> findByName(String name) {
        // name은 중복이 가능하기때문에 stream()으로 돌려서 찾기
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny();

    }

    @Override
    public List<Member> findAll() {

        return new ArrayList<>(store.values());
    }

    public void clearStore(){
        store.clear();
    }
}

 

 

테스트코드)

*Assertions 는 junit 이 아닌 assertj 에 있는 것을 사용함

package hello.hellospring.repository;

import ..

class MemoryMemberRepositoryTest {
    
    MemoryMemberRepository repository = new MemoryMemberRepository();

    // 테스트마다 서로 의존관계없이 설계되야함,
    // 테스트 하나 끝날때마다 저장소나 공용데이터를 지워야한다
    @AfterEach
    public void afterEach(){
        repository.clearStore();

    }
    @Test
    public void save(){
        Member member = new Member();
        member.setName(("spring"));
        repository.save(member);
        // ***
        Member result = repository.findById(member.getId()).get();
        //Assertions.assertEquals(member, result);
        //Assertions.assertThat(result).isEqualTo(member);
        assertThat(result).isEqualTo(member);

    }
    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

		// ***
        Member result = repository.findByName("spring1").get(); // get()하면 옵셔널 까서꺼낼수있다

        assertThat(result).isEqualTo(member1);

    }

++ 보충설명

// 원래 반환타입은 Optional
Optional<Member> result = repository.findById(member.getId());

// get() 을 이용하면 옵셔널을 까서 꺼낼 수 있다(테스트코드에서는 종종 사용)
Member result = repository.findById(member.getId()).get();

 

 

위의 테스트 이어서)

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        List<Member> result = repository.findAll();
        assertThat(result.size()).isEqualTo(2);
    }

}

 

 

출처: https://mangkyu.tistory.com/70 [MangKyu's Diary]