박싱된 기본 타입보다는 기본 타입을 사용하라

박싱된 기본 타입은 주의해서 사용하자

Posted on 2019-05-10

박싱된 기본 타입보다는 기본 타입을 사용하라


자바의 데이터 타입은 크게 기본 타입과 참조 타입으로 나눌 수 있다.

  • 기본 타입 : int, double, boolean ..
  • 박싱된 기본 타입 : Integer, Double, Boolean ..

오토박싱오토언박싱 덕분에 두 타입을 크게 구분하지 않고 사용할 수 있다.
하지만 분명한 차이가 있으니 어떤 타입을 사용하는지는 상당히 중요하다.


두 타입의 차이점

  1. 기본 타입은 값만 가지고 있으나, 박싱된 기본 타입은 값에 더해 식별성이란 속성을 갖는다. 즉, 값이 같아도 서로 다르다고 식별될 수 있다.
  2. 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 null을 가질 수 있다.
  3. 기본 타입이 박싱된 기본 타입보다 시간메모리 사용면에서 더 효율적이다.

박싱된 기본 타입 오용 사례 1

오름차순으로 정렬하는 비교자 구현 예제

// 잘못 구현된 비교자
Comparator<Integer> naturalOrder = 
    (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);

int value = naturalOrder.compare(new Integer(42), new Integer(42));
assertThat(value).isNotEqualTo(0);
assertThat(value).isEqualTo(1);

두 인스턴스의 값이 같으므로 0을 출력해야 하지만, 실제로는 1을 출력한다.
i == j 에서 식별성 검사를 하기 때문이다.
이처럼 박싱된 기본 타입에 == 연산자를 사용하면 오류가 발생한다.

// 문제를 수정한 비교자
Comparator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
    int i = iBoxed;
    int j = jBoxed;
    i < j ? -1 : (i == j ? 0 : 1);   
}

int value = naturalOrder.compare(new Integer(42), new Integer(42));
assertThat(value).isEqualTo(0);
assertThat(value).isNotEqualTo(1);

기본 타입을 다루는 비교자가 필요하다면 Comparator.naturalOrder() 를 사용하자.
비교자를 직접 만들면 비교자 생성 메서드나 기본 타입을 받는 정적 compare 메서드를 사용해야 한다.


박싱된 기본 타입 오용 사례 2

조건문 동일 비교 구현 예제

public class Test {
    static Integer i;
    
    public static void main(String[] args){
        if (i == 42)
            System.out.print("호출되지 않는 라인");
    }
}

기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다. null 참조를 언박싱하면 NullPointerException 이 발생한다.


박싱된 기본 타입 오용 사례 3

0 부터 Integer 최댓값 까지의 합 구현 예제

public static void main(String[] args){
    Long sum = 0L; // 박싱된 기본 타입 선언
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i; // 박싱과 언박싱 발생
    }
}

위와 같이 지역변수 sum을 박싱된 기본 타입으로 선언하는 바람에 sum을 구하는 과정에서 반복적으로 박싱과 언박싱이 발생하여 성능이 느려진다.


박싱된 기본 타입 사용 사례

  1. 컬렉션의 원소, 키, 값으로 사용해야 한다. 컬렉션은 기본 타입을 지원하지 않는다.
  2. 리플렉션을 통해 메서드를 호출할 때 사용해야 한다.

핵심 정리

간단하고 빠른 기본 타입을 사용하되 박싱된 기본 타입을 사용한다면 주의를 기울이자. 오토박싱이 박싱된 기본 타입을 사용할 때의 번거로움을 줄여주지만, 그 위험까지 없애주지는 않는다. 언박싱 과정에서 NPE가 발생하거나 불필요한 박싱으로 객체가 생성될 수 있다.


Reference
  • Effective Java