Effective Java를 읽고 정리한 정리본입니다.

📌 Item 61 : 박싱된 기본 타입보다는 기본 타입을 사용하라

자바의 데이터 타입은 다음과 같은 두 가지가 존재한다.

  1. 기본 타입 : int, double, boolean 등
  2. 참조 타입 : String, List 등

🫧 기본 타입과 박싱된 기본 타입 차이

기본 타입 박싱된 기본 타입
값만 가지고 있음 식별성 존재
null 비허용 null 허용
효율적 (시간, 메모리) 비효율적

🫧 첫 번째 문제 : 식별성 존재

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

이러한 특징으로 인해, 같은 객체를 비교하는 것이 아니라면 박싱된 기본 타입에 == 연산자를 사용하면 오류가 발생하게 된다.

✨ 해결법

실무에서 이와 같이 기본 타입을 다루는 비교자가 필요하다면 Comparator.naturalOrder()을 사용하자.

지역변수 2개를 두어 각각 박싱된 Integer 매개변수의 값을 기본 타입 정수로 저장한 다음, 모든 비교를 이 기본 타입 변수로 수행하면 오류의 원인인 식별성 검사가 이뤄지지 않는다.

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

🫧 두 번째 문제 : null 참조

다음과 같은 코드를 보자.

public class Unbelievable {
  static Integer i;

  public static void main(String[] args) {
    if (i == 42) 
      System.out.println("믿을 수 없군!");
  }
}

i==42를 검사할 때 i는 Integer 타입의 객체이며, 해당 코드에서 Integer과 int를 비교하고 있다.

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

✨ 해결법

  • i를 int로 선언하기

🫧 세 번째 문제 : 쓸모 없는 객체 생성 비용 발생

public static void main(String[] args) {
  Long sum = 0L;
  for (long i=0; i<=Integer.MAX_VALUE; i++) {
    sum += i;
  }
  System.out.println(sum);
}

이 프로그램은 실수로 지역변수 sum을 박싱된 기본 타입으로 선언하여 느려졌다.

오류나 경고 없이 컴파일되지만, 박싱과 언박싱이 반복해서 일어나 체감될 정도로 성능이 느려진다.

🫧 박싱된 기본 타입을 쓰는 경우

  1. 컬렉션의 원소, 키, 값으로 사용
  2. 매개변수화 타입 or 매개변수화 메서드의 타입 매개변수
  3. 리플렉션을 통해 메서드 호출