자바는 두 가지 타입 시스템을 가지고 있다.
- 기초 자료형(primitive types), int, double, boolean이 기초형에 속한다.
- 레퍼런스 타입(reference types), List, Set 등이 레퍼런스에 속한다.
이중에서 기초 자료형의 오토박싱과 오토언박싱은 중요한 차이점이 있다.
기초자료형(primitive types)과 박싱된 기초자료형(boxed primitive types)에는 3가지 주요 차이점이 있다.
- 기초자료형은 값만을 가질 수 있다. 반면, 박싱 타입은 값 자체와는 구별되는 식별성(identity)을 갖는다.
- 기초자료형은 완전한 함수적 값이지만, 박싱 타입은 하나의 함수적이지 않은 값, null을 취급할 수 있다.
- 기초자료형은 박싱 타입보다 시간 효율성과 공간 효율성이 높다.
다음 코드는 두 정수값을 받아 비교하는 함수다. 이 코드의 문제점을 말할 수 있는가?
Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1: (i == j ? 0 : 1)
두 정수, 예를 들어 42를 동일한 두 인자로 전달하여 이 함수를 호출해보자.
naturalOrder.compare(new Integer(42), new Integer(42))
결과는 0을 기대했지만, 실제로 출력되는 값은 1이다.
이 코드의 문제는 ==
에 있다. ==
연산은 레퍼런스 타입일 경우 동일성(identity)를 판단한다.
따라서, 42라는 값에 대해 ==
연산을 하는 것이 아니라, 레퍼런스의 동일성을 판단하는 것이다.
박싱 타입 ==
을 사용하는 것은 거의 항상 잘못이다.
그렇다면, 이 코드를 정상적으로 동작하기 위해서 어떻게 수정해야할까?
박싱 타입을 언박싱하여 비교하면 된다!
Comparator<Integer> naturalOrder = (boxedI ,boxedJ) -> {
int i = boxedI, j = boxedJ;
return (i < j) ? -1 : (i == j) ? 0 : -1;
};
마지막으로 다음 코드의 문제점을 파악해보자.
public static void main(String[] args) {
long startTime = System.nanoTime();
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += 1;
}
long endTime = System.nanoTime();
double elapsedTimeSeconds = (endTime - startTime) / (double) 1_000_000_000;
System.out.println("how long did it take? : " + elapsedTimeSeconds + " seconds");
}
문제는 매우 느리다는 것이다. 결과는 5초 가량이 소요된다.
how long did it take? : 5.332838373 seconds
main 함수의 두 번째 줄의 Long sum
을 long sum
으로 바꾸어 결과를 확인하면, 1초 미만이 소요된다.
how long did it take? : 0.683741564 seconds
컬렉션의 항목/키/밸류에 사용한다. 파라미터화 타입(parameterized type)에는 기초 자료형을 사용할 수 없다.
ThreadLocal<int>
는 불가능하고 ThreadLocal<Integer>
로 작성해야 된다는 것이다.
- 박싱 타입은 값 타입이 아닌 레퍼런스 타입에 속한다.
- 박싱 타입은 레퍼런스 타입이므로,
==
연산 시에 동일성(identity)이 계산된다. - 박싱 타입은 연산이 매우 느리다.
- 박싱 타입과 기초자료형을 섞어서 쓰면 안된다. 널 예외가 발생할 위험이 있다.