Effective Java

[9장] Item 61. 박싱된 기본 타입보다는 기본 타입을 사용하라

앙두딘 2022. 7. 29. 19:18

9장. 일반적인 프로그래밍 원칙


 

기본타입은 간단하고 빠르다.
기본타입, 박싱된 기본 타입을 혼용할 경우,
언박싱 과정에서 NullPointerException을 던질 수 있다.

 

 

 

자바의 데이터 타입은 기본타입(int, double, boolean), 박싱된 기본 타입(Integer, Double, Boolean)이 있다.

 

<두 타입의 주된 차이>는 다음과 같다.

 

1. 식별성

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

즉, 박싱된 기본 타입은 참조 타입 변수이기 때문에 

Integer i1 = new Integer(1);
Integer i2 = new Integer(1);

두 변수 i1, i2가 같은 값 1을 가지고 있더라도 "i1 == i2" 는 false인 것이다.

값이 같아도 서로 다르다고 식별될 수 있다.

 

2. 박싱된 기본 타입은 유효하지 않은 값, 즉 null값을 가질 수 있다.

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

 

이 차이점들을 잘 유의하고 사용해야 한다.

 

 

compare메소드는 첫번째 원소가 두번째 원소보다 작으면 음수, 같으면 0, 더 크면 양수를 리턴한다.

하지만 다음 코드는 0이 아닌 1을 출력한다.

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

42로 값이 같음에도 1을 출력하는 이유는 다음과 같다.

i<j 조건을 검사할 때는 오토언박싱으로 값을 비교해 알맞은 결과가 나온다.

하지만 i==j조건을 검사할 때는 i, j가 서로 다른 Integer 인스턴스이므로 다르다는 결과가 나온다.

따라서, i가 j보다 크다는 잘못된 결과가 나오는 것이다.

이처럼 같은 객체를 비교하는 것이 아니라면 박싱된 기본 타입에 ==연산자를 사용하면 오류가 일어난다.

 

오토 박싱, 오토 언박싱이란?

더보기

Integer num = new Integer(17); // 박싱

int n = num.intValue();        // 언박싱

System.out.println(n);

 

Character ch = 'X'; // Character ch = new Character('X'); : 오토박싱

char c = ch;        // char c = ch.charValue();           : 오토언박싱

System.out.println(c);

 

 

문제를 해결하려면 지역변수 2개를 두어 각각 박싱된 Integer 매개변수의 값을 기본 타입 정수로 저장한 다음, 모든 비교를 이 기본 타입 변수로 수행한다.

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

 

 

박싱된 기본 타입은 다음과 같은 코드에서 NullPointerException을 발생시킨다.

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

i가 null값을 가지기 때문이다.

박싱된 기본 타입도 다른 참조 타입 필드처럼 초깃값이 null이다.

 

기본타입과 박싱된 기본타입을 혼용한 대부분의 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다.

하지만, 위 경우처럼 null참조를 언박싱하면 NullPointerException이 발생한다.

 

 

박싱, 언박싱이 반복적으로 일어나는 코드는 체감될 정도로 성능이 느려진다.

따라서 박싱된 기본 타입은 유의해서 사용해야 한다.

 

박싱된 기본 타입이 사용되는 경우?

1. 컬렉션의 키, 원소 값으로 사용한다.

컬렉션은 기본 타입을 담을 수 없다. 따라서 어쩔 수 없이 박싱된 기본 타입을 써야만 한다.

 

2. 매개변수화 타입이나 매개변수화 메서드의 타입 매개변수로는 박싱된 기본 타입을 써야 한다.

자바 언어가 타입 매개변수로 기본 타입을 지원하지 않기 때문이다.

ex) ThreadLocal<Integer>

 

3. 리플렉션을 통해 메서드를 호출할 때도 박싱된 기본 타입을 사용해야 한다.

 

기본 타입은 간단하고 빠르다. 
둘 중 하나를 선택해야 한다면 가능하면 기본 타입을 사용하라.

오토박싱이 박싱된 기본 타입을 사용할 때의 번거로움을 줄여주지만, 그 위험까지 없애주지는 않는다.
(언박싱 과정의 NullPointerException, 기본 타입을 박싱할 때 필요 없는 객체를 생성하는 부작용)