ITEM 61: 박싱된 기본 타입보다는 기본 타입을 사용해라

  • 기본타입 : int, double, long, boolean 등등

  • 참조 타입 : String, List 등등

    • 박싱된 기본 타입 : 각각 기본 타입에 대응하는 참조타입 (Integer, Long, Boolean)

기본 타입 vs 박싱된 기본 타입

기본 타입은 값만 갖고 있으나, 박싱된 기본 타입은 값 + 식별성(identity) 속성을 갖는다.

즉, 박싱된 기본 타입의 두 인스턴스의 값이 같아도 서로 다르다고 식별될 수 있다.

Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
System.out.println(naturalOrder.compare(new Integer(42), new Integer(42))); // 1

i == j에서 객체 참조의 식별성을 검사하게 된다. 그래서 비교 결과가 false로 나오고, 비교자는 1을 반환하는 것이다. 같은 객체를 비교하는게 아니라면 박싱된 기본 타입에 == 연산자를 사용하면 오류가 발생한다.

Comparator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
  int i = iBoxed;
  int j = jBoxed;
  return (i < j) ? -1 : (i == j ? 0 : 1);
};

기본 타입으로 값을 저장한 다음 비교하면, 식별성 검사가 이루어지지 않아 정상적인 값이 나온다.

기본 타입의 값은 언제나 유효하지만, 박싱된 기본 타입은 유효하지 않은 값 null을 가질 수 있다.

Integer i;

if(i == 42) { // NullPointerException
  System.out.println("당첨");
}

여기서 i는 int가 아닌 Integer로 다른 참조 타입과 마찬자기로 초기값이 null이기 때문에 발생하는 오류이다. 기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀리며, null을 참조하는 값을 언박싱하면 NullPointerException이 발생하는 것이다.

int i; // 0

if(i == 42) {
  System.out.println("당첨");
}

기본 타입이 박싱된 기본 타입보다 시간과 메모리 사용면에서 더 효율적이다.

Long sum = 0L;

for (long i = 0; i <= Integer.MAX_VALUE; i++){
  sum += 1;
}

지역변수 sum을 박싱된 기본타입으로 선언하여 느려진 코드이다. 오류나 경고 없이 컴파일 되지만, 박싱과 언박싱이 반복해서 일어나 체감될 정도로 성능이 느려진다.

이러한 3가지 차이점 때문에 항상 주의하고 사용해야한다.

박싱된 기본타입을 사용해야하는 경우

  1. 컬렉션의 원소, 키, 값으로 사용 : 컬렉션은 기본타입을 담을 수 없어 박싱된 기본타입을 사용해야한다.

  2. 매개변수화 타입이나 매개변수화 메서드의 타입 매개변수로 사용 : 자바 언어가 타입 매개변수로 기본 타입을 지원하지 않음

  3. 리플렉션을 통해 메서드를 호출할 때 사용

정리

기본 타입은 간단하고 빠르므로, 가능하다면 기본타입을 사용하는 것이 좋다. 오토 박싱은 박싱된 기본 타입을 사용할 때 번거러움을 줄여주지만, 리스크까지 없애주지 않는다.

Last updated