본문 바로가기

회고/이펙티브 자바 3판

[ 아이템 61 ] 박싱된 기본 타입보다는 기본 타입을 사용하라

반응형

 자바의 타입은 int, double, boolean 같은 기본 타입과 String, List 같은 참조 타입이 있다. 기본 타입은 대응하는 참조 타입이 있으며, 이것은 박싱된 기본 타입이다. int -> Integer, double -> Double, boolean -> Boolean.

 

 기본 타입과 박싱된 기본 타입은 크게 3가지 차이점이 있다.

1. 기본 타입은 값만 있으나, 박싱된 기본 타입은 식별성(identity)이 있어서 값이 같아도 다르게 식별될 수 있다.

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

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

 

 잘못 구현된 비교자 - 문제를 찾아보자

Comparator<Integer> naturalOrder = 
	(i,j) -> (i<j)? -1 : (i==j ? 0 : 1);

1. 식별성(identity) 검사의 문제

 결과를 알기 위해서 naturalOrder.compare(new Integer(42), new Integer(42))를 실행해보자. 두 Integer의 값이 42로 같다고 생각되어 0을 출력해야 하지만, 실제로는 1을 출력한다. 원인이 뭘까?

 

 ( i < j ) 는 잘 작동한다. 여기서 i와 j가 참조하는 오토박시된 Integer 인스턴스는 기본 타입 값으로 변환된다. 그런 다음만약 i가 j보다 작지 않다면 두번째 검사( i == j )가 아뤄진다. 이 검사는 두 '객체 참조'의 식별성을 검사하게 된다. i와 j가 서로 다른 Integer 인스턴스라면, 비록 값은 같더라도 비교 결과는 false가 되고, (잘못된 결과인) 1을 반환한다. 이처럼 같은 객체를 비교하는 것이 아니라면 박싱된 기본 타입에 == 연산자를 사용하면 오류가 일어난다.

 

오류의 원인인 식별성 검사에서 자유로워 지려면, 다음과 같은 수정이 필요하다.

 

문제를 수정한 비교자

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

 

2. null 값을 가지는 문제

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

 이 프로그램은 아무것도 출력하지 않지만, i ==42를 검사할 때 NullPointerException을 던진다. 원인은 i가 int가 아니라 Integer이며 초기값이 null이기 때문이다. 기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다. i를 int로 선언해주면 된다.

 

3. 박싱된 타입의 시간과 메모리 효율서의 문제

끔찍이 느리다! 객체가 만들어지는 위치를 찾았는가?

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을 박싱된 기본타입으로 선언하였다는 것이다. 오류나 경고 없이 컴파일되지만, 박싱과 언방식이 반복해서 일어나 체감될 정도로 성능이 느려진다.

 

박싱된 기본 타입은 언제 써야 하는가?

첫 번째, 컬렉션의 원소 , 키, 값으로 쓴다. 컬렉션은 기본 타입을 담을 수 없으므로 어쩔 수 없다. 자바 언어가 매개변수로 기본 타입을 지원하지 않기 때문이다. 두 번째, 리플렉션을 통해 메서드를 호출할 때도 박싱된 기본 타입을 사용해야 한다.

 

 

*느낀점

기본타입과 박싱된 기본타입의 2가지 형태는 잘 유의해야겠다. 일단 기본타입을 고려하고 사용해야 한다는 것을 알았다. 대신 컬렉션에 관한 사용은 박싱을 항상 고려해야 겠다. 박싱된 기본타입은 객체의 모양을 가지기 때문에 ==으로는 비교가 안된다는 것과 null을 갖는다는 사실도 알면, 훨씬 오류 상황에 대한 예방을 할 수 있을 것이다.

반응형