반응형

가독성 높이는 습관 - null 핸들링

 

null check - 단정문 (Assertion)

assert expression;
assert expression1; expression2;
  • JDK 1.4+
  • expression1이 거짓이면, java.lang.AssertionError 발생
  • espression2는 위 에러에 포함될 정보
  • 단정문을 활성화하려면, jaca -ea 옵션을 주어야함
  • 사용하지 말 것 !

 

 

null check - Objects (JDK 1.8+)

Boolean Check

  • boolean isNull(Object obj);
  • boolean nonNull(Object obj);

Fail Fast

  • T requireNonNull(T obj)
  • T requireNonNull(T obj, String message)
  • T requireNonNull(T obj, Supplier<String> messageSupplier)

 

public static boolean isNull(Object obj) {
    return obj == null;
}

public static boolean NonNull(Object obj) {
    return obj != null;
}

 

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

 

 

null 표현하기 - 타입

Primitive type

  • boolean
  • char
  • int
  • long
  • float
  • double

Reference type

  • 기본 타입의 Wrapper class

 

두 가지 모두 null이 될 수 없다는 특징이 있지만, 메모리 위치가 다름

  • 기본 타입은 메모리의 스택 영역에 위치
  • 레퍼런스 타입은 스택 영역에는 참조값만 존재하고 데이터는 힙 영역에 위치

 

 

Primitive, Reference type으로 표현해보기

  • A가 B에게 포인트를 선물하는 API의 Request 객체 만들기
public class GiftPointRequest {
    // primitive type -> !null
    private long userId;
    private long point;
    
    // class type
    private String memo;
    private Object type;
}

 

 

null 표현하기 - 다른 언어 사례 (Kotlin)

var str: String = "null 불가능한 타입"
var nullableStr: String? = "nullable 타입"

str = null  // 컴파일 에러
nullableStr = null  // ok

println(str.length)
println(nullableStr?.length)  // safe call

// elvis operator
println(nullableStr?.length ?: 0)
println(nullableStr?.length ?: throw RuntimeException("문자열이 비었습니다."))

 

 

그 밖의 null 핸들링 방법들

  • 1. 선언과 동시에 초기화
  • 2. 다양한 유틸클래스 활용 (스프링, 구아바, 아파치 커먼즈, ...)
  • 3. null 파라미터가 존재한다면, 오버로딩으로 교체
  • 4. List<T> 일 때는 빈 리스트 활용
  • 5. 상황에 따른 Null Object Pattern 활용
  • 최대한 직접적인 핸들링은 지양

 

 

선언과 동시에 초기화

// C style
Long getCalcAmount(long pid, long cid) {
    Product product = null;
    Coupon coupon = null;
    long amount = 0;
    
    product = findProduct(pid);
    coupon = findCoupon(cid);
    
    amount = coupon.discount(product);
    return amount;
}
// 선언과 동시에 초기화
Long getCalcAmount(long pid, long cid) {

    Product product = findProduct(pid);
    Coupon coupon = findCoupon(cid);
    
    long amount = coupon.discount(product);
    return amount;
}

 

 

 

null 파라미터 대신 오버로딩

// 잘못된 코드
Product findProduct(Long refId, String somethingStr, Long otherId ...) {
    if (refId != null)
        return repository.findByRefId(refId);
    else if (somethingStr != null && otherId != null)
        return repository.findBySomethingStrAndOtherId(somethingSter, otherId);
    // ... 생략
}
Product findProduct(Long refId) {
    return repository.findByRefId(refId);
}

Product findProduct(String somethingStr, Long otherId) {
    return repository.findBySomethingStrAndOtherId(refId);
}

 

 

비어있는 리스트 활용

List<Coffee> getCoffeeList(/* something parameter */) {
    if (invalid())
        return null;
    // 비지니스 로직 수행
    
    return coffeeList; // 로직 결과
}

// 위 메서드를 사용하는 곳에서
List<Coffee> coffeeList = getCoffeeList();
if (coffeeList != null)
    // 비지니스 로직 수행
List<Coffee> getCoffeeList(/* something parameter */) {
    if (invalid())
        return Collections.emptyList();
    // 비지니스 로직 수행
    
    return coffeeList; // 로직 결과
}

// 위 메서드를 사용하는 곳에서
getCoffeeList()
        .stream()
    // 비지니스 로직 수행

 

 

Null Object Pattern

 

Null Object Pattern

design patterns, null object pattern

dev.to

 

 

 

정리

  • 선언과 동시에 초기화가 최고 (null 발생하지 않도록)
  • Assertion 문은 사용하지 말자
  • Null은 되도록 직접 핸들링 하지말자
  • JDK 1.8+ 에서 제공하는 유틸을 적극 활용 (Objects)
  • Primitive type, Reference type 으로 nullable 여부 나타내기
  • List 는 null 대신 emptyList() 사용
  • null 을 대체할 static 객체 리턴 (null object pattern)

 

 


반응형

+ Recent posts