Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 Weeks - [Java의 null 처리] #57

Open
hgene0929 opened this issue Sep 2, 2023 · 1 comment
Open

4 Weeks - [Java의 null 처리] #57

hgene0929 opened this issue Sep 2, 2023 · 1 comment
Assignees

Comments

@hgene0929
Copy link
Collaborator

문제

Java의 null 처리 방법에는 Optional 이외에도 requiredNonNull, assert 등이 있는 것으로 알고 있습니다.
이들과 Optional은 각각 어떻게 사용되는지 차이점이나 특징, 사용사례 등에 대해 알고싶습니다.

contents - 세부 내용

Optional을 다루는 김에 Java의 null 처리 방법에 대해 정리하고 싶습니다.

@devjy39
Copy link
Member

devjy39 commented Sep 6, 2023

Null이란 무엇인가

  • java에선 참조 타입의 변수가 아무런 객체를 가리키지 않을 때 null이라고 합니다.
  • java application에서 가장 많이 발생하는 error는 NullPointerException이라고 합니다.

null을 피할려면?

if(object == null) ? null 이라면..
  • 매번 회피 로직을 넣어야 한다. 컴파일 타임에 발견되지 않을 수도 있기에 매우 위험.

Null handling 방법

선언과 동시에 초기화하기

Student student = new Student();
  • 코드를 읽을 때 변수의 변경 포인트(side effect)가 적어지기 때문에 가독성이 좋아집니다.

java 1.8에서 추가된 Objects 클래스의 하위 메서드 사용

if(student == null) {
    boolean isNull = true; 
} // 기존의 로직

boolean isNull = Objects.isNull(student); 
boolean isNotNull = Objects.nonNull(student);
  • Objects.isNull(), Objects.nonNull()
  1. 내부 로직은 단순 논리연산자로 null을 체크.
  2. null은 되도록 직접 핸들링 하지 않는 게 좋습니다.
  3. null check 로직을 넣을 거면 Objects의 메서드로 가독성 좋게 만들자.
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}
  • Objects.requireNonNull()
  1. null이면 nullPointerException을 던진다.
  2. 명시적으로 null이 아님을 입증할 때 사용한다.

변수 Type으로 관리

  • Reference type은 null을 가질 수 있다.
    • 선언 시 데이터는 메모리의 힙 영역에 저장되고 참조 값만 stack영역에 저장됨.
  • 하지만 Primitive type은 null을 가질 수 없다.
    • 선언 시 메모리의 stack영역에 저장된다. (멤버변수 x)
    • 반드시 인자가 들어오는 경우에 primitive type을 선언하면 null 체크를 하지 않아도 된다.

메서드 오버로딩

static class Student {
        String name;
        String certification;
        Address address;

        public Student(String name, String certification, Address address) {
            this.name = name;
            this.certification = Objects.isNull(certification) ? "없음" : certification;
            this.address = Objects.isNull(address) ? Address.isEmpty() : address;
        }
}

// 메서드 오버로딩으로 null 체크를 없애자.

public Student(String name) {
    this.name = name;
}

public Student(String name, String certification) {
    this.name = name;
    this.certification = certification;
}

public Student(String name, Address address) {
    this.name = name;
    this.address = address;
}

public Student(String name, String certification, Address address) {
    this.name = name;
    this.certification = certification;
    this.address = address;
}
  • 필요한 파라미터들끼리만 함수를 다양하게 구현한다.
  • null 체크를 하지 않아도 되고 가독성에도 좋음

비어있는 List 활용

public static final <T> List<T> emptyList() {
		return (List<T>) EMPTY_LIST;
}

// 자매품
Collections.emptyMap();
Collections.emptySet();
  • List를 반환하는 메서드는 null을 반환하면 안된다 → 호출 쪽에서 (NullPointerException 문제)
    • empty immutable list가 필요하다면 Collections.emptyList()
    • mutable list가 필요하다면 new ArrayList() 같은 빈 list를 반환하자.

Optional

  • java 1.8에서 추가 되었다. null을 직접 핸들링 하지 않아도 된다.
  • 타입만으로 null 여부를 나타내기 가능해진다.
    • Optional변수는 null일 수 없으며 항상 optional 인스턴스를 가르켜야한다.
      • oracle : Optional에 null을 넣지 말고 항상 Optional.empty()를 넣어야 한다.
  • 체이닝을 통한 다양한 중간 및 종단 처리를 지원한다.
  • Optional은 직접 생성하는 게 아니다.
    생성자가 private로 막혀있고, static 팩토리 메서드를 제공한다.
// private Constructor
private Optional() {
    this.value = null;
}

private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}

// static factory
public static<T> Optional<T> empty() {
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
}

public static <T> Optional<T> of(T value) { // -> value에 null이 들어가면 NPE!
        return new Optional<>(value);
}

public static <T> Optional<T> ofNullable(T value) { // null 가능성이 있다면 사용
        return value == null ? empty() : of(value);
}

optional 사용 시 주의사항

  • 멤버 변수에 Optional 선언은 안티 패턴이다.

    • optional은 함수의 반환 목적으로 만들어져있기 때문.
    • Serializable 구현하지 않았으므로 직렬화되지 않음
      • 직렬화란? 객체를 다른 시스템으로 보내기 위해 byte stream으로 바꾸는 것
      • 가상 메모리를 사용하기 때문에 논리 주소와 실제 주소가 다를 수 있는데,
        만약 객체 정보를 그대로 저장하면 가상 메모리 주소까지 저장되게 된다.
        메모리 주소는 다른 시스템에 전달하는 의미가 없다.
        그렇기에 이러한 불필요한 정보를 제외하고 타입, 값 정보를 byte형태로 전달하는 것을 직렬화라고 한다. 그리고 Serializable을 구현해야 한다.
    • optional.empty()를 빠뜨릴 확률이 높음
      • 자바 객체의 멤버 변수(reference타입)의 기본값은 null인데 optional은 null을 넣지 말라고 했다. 이 부분을 위해 매번 체크하고 optional.empty를 넣어야만 한다.
  • collection에는 optional을 사용하지 말자.

    • collections에서 이미 null에 안전한 로직이 구현되어 있기 때문.
    • optional 말고 Collections.empty..()를 활용하자.
  • optional을 파라미터로 넘기지 않기

    • 앞서 null을 다루는 방법에서처럼 매번 사용하지 않는 파라미터를 선언하는 것이므로,
      • 메서드 오버로딩으로 처리하는 게 좋다.
  • 종단 메서드를 사용하기 위해 너무 남용하면 오히려 가독성이 떨어질 수 있다.

@devjy39 devjy39 self-assigned this Sep 6, 2023
@devjy39 devjy39 closed this as completed Sep 8, 2023
@devjy39 devjy39 reopened this Dec 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants