
package com.hello.hellospring.domain;
public class Member {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.hello.hellospring.repository;
import com.hello.hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
개발을 할 때 가장 많이 발생하는 예외 중 하나가 바로 NPE(NullPointerException)이다. NPE를 피하려면 null 여부를 검사해야 하는데, null 검사를 해야하는 변수가 많은 경우 코드가 복잡해지고 번거롭다. 그래서 null 대신 초기값을 사용하길 권장하기도 한다.
Optional는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 도와준다. Optional 클래스는 아래와 같은 value에 값을 저장하기 때문에 값이 null이더라도 바로 NPE가 발생하지 않으며, 클래스이기 때문에 각종 메소드를 제공해준다.
public final class Optional<T> {
// If non-null, the value; if null, indicates no value is present
private final T value;
...
}
Optional<String> optional = Optional.empty();
System.out.println(optional); // Optional.empty
System.out.println(optional.isPresent()); // false
// Optional의 value는 절대 null이 아님
Optional<String> optional = Optional.of("hello");
// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("anonymous"); // 값이 없다면 "anonymous" 를 리턴
package com.hello.hellospring.repository;
import com.hello.hellospring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository{
// 키, 값
// 실무에서는 동시성 문제 때문에 사용하지 않음(ConcurrnetHashMap 사용)
private static Map<Long, Member> users = new HashMap<>();
// 실무에서는 동시성 문제 고려해야함
private static long sequence = 0L;
@Override
public Member save(Member member) {
// 시퀀스 값 올려주기
member.setId(++sequence);
// users에 저장
users.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
// .ofNullable : 널값을 허용함
return Optional.ofNullable(users.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return users.values().stream().filter(x -> x.getName().equals(name)).findAny();
}
@Override
public List<Member> findAll() {
// users에 저장되어있는 value들의 값을 리스트로 변환 후 반환
return new ArrayList<>(users.values());
}
}
스트림은 '데이터의 흐름’입니다. 배열 또는 컬렉션 인스턴스에 함수 여러 개를 조합해서 원하는 결과를 필터링하고 가공된 결과를 얻을 수 있습니다. 또한 람다를 이용해서 코드의 양을 줄이고 간결하게 표현할 수 있습니다. 즉, 배열과 컬렉션을 함수형으로 처리할 수 있습니다.
또 하나의 장점은 간단하게 병렬처리(multi-threading)가 가능하다는 점입니다. 하나의 작업을 둘 이상의 작업으로 잘게 나눠서 동시에 진행하는 것을 병렬 처리(parallel processing)라고 합니다. 즉 쓰레드를 이용해 많은 요소들을 빠르게 처리할 수 있습니다.
참고 :
✅ Stream과 Optional
✅ Stream 클래스 더 알아보기!!