Reference/Effective-Java

[Effective-Java] Chapter9 #61. 박싱된 기본 타입보다는 기본 타입을 사용하라

나죽못고나강뿐 2023. 7. 20. 11:43
📌 Java의 데이터 타입
  • 기본 타입 : int, double, boolean 등
  • 참조 타입 : String, Integer, Double, List 등
  • 기본 타입은 모두 대응되는 참조타입이 있으며, 이를 박싱된 기본 타입이라 한다.
  • Auto Boxing/Unboxing 덕에 두 타입을 구분하지 않고 사용할 수는 있지만, 주의해서 선택해야 한다.

 

📌 기본 타입 vs 박싱된 기본 타입
final int number1 = 1;
final int number2 = 1;

System.out.println(number1 == number2); // true

final Integer number3 = 1;
final Integer number4 = 1;
System.out.println(number3 == number4); // true
System.out.println(number3.equals(number4)); // true

final Integer number5 = new Integer(1);
final Integer number6 = new Integer(1);

System.out.println(number5 == number6); // false
System.out.println(number5.equals(number6)); // true
int number = null // 에러
Integer number = null; // 가능
  1. 기본 타입은 값만 가지지만, 박싱된 기본 타입은 값과 식별성(identity)이란 속성을 갖는다
  2. 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 값인 null을 가질 수 있다.
  3. 기본 타입이 박싱된 기본 타입보다 시간·메모리 측면에서 효율적이다.

 

📌 박싱된 기본 타입 이슈 3가지

1️⃣ 식별성(identity)

💡 박싱된 기본 타입에 == 연산자를 사용하면 오류가 일어난다.

아래는 Integer 값을 오름차순으로 정렬하는 비교자

Comprator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
  • 위 비교자의 심각한 결함이 궁금하다면, naturalOrder.compare(new Integer(42), new Integer(42))를 출력해보라. (1을 출력한다.)
  • i == j 에서 값이 아닌, 두 객체 참조의 식별성을 검사하게 되어 잘못된 결과가 나온다.
Comprator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
	int i = iBoxed, j = jBoxed;
	return (i < j) ? -1 : (i == j ? 0 : 1);
}
  • 지역변수로 Auto Unboxing을 수행하고, 기본 타입 연산을 수행하면 식별성 검사가 이뤄지지 않는다.
  • 애초에 equals를 사용하면 되는 부분이기도 하다.

 

2️⃣ 유효하지 않은 값(null)

💡 기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다.
public class Unbelievable {
    static Integer i;
    
    public static void main(String[] args) {
        if (i == 42)
            System.out.println("믿을 수 없군!");
    }
}
  • 기본 타입인 42와 비교하면 i는 자동으로 Unboxing이 되는데, 초기값을 지정해주지 않아 NullPointerException이 발생한다.
  • 초기값을 설정해주거나, Integer를 int로 선언해주었으면 적어도 NPE가 발생하지는 않았을 것이다.

 

3️⃣ Auto Boxing/Unboxing

public static void main(String[] args) {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;
    System.out.println(sum);
}
  • 오류가 발생하진 않지만 auto boxing/unboing이 반복해서 일어나 성능이 현저하게 저하된다. (Item 6. 불필요한 객체 생성을 피하라)

 

📌 박싱된 기본 타입을 사용하는 경우
  • Collection의 원소, 키, 값으로 쓰는 경우 (Collection은 기본 타입을 담을 수 없으므로)
  • 매개변수화 타입이나 매개변수화 메서드(5장)의 타입 매개변수인 경우
  • 리플렉션(Item 65)을 통해 메서드를 호출하는 경우