오토박싱과 오토언박싱 덕분에 기본 타입과 참조 타입을 크게 구분하지 않고 사용할 수는 있지만, 그렇다고 차이가 사라지는 것은 아니다.
둘 사이에는 분명한 차이가 있다.
어떤 타입을 사용하는지는 상당히 중요하다.
주의해서 선택해야 한다.
기본 타입과 박싱된 타입의 주된 차이는 크게 세 가지다.
첫 번째, 기본 타입은 값만 가지고 있으나, 박싱된 기본 타입은 값에 더해 식별성이란 속성을 갖는다.
달리 말하면 박싱된 기본 타입의 두 인스턴스는 값이 같아도 서로 다르다고 인식될 수 있다.
두 번째, 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 값, 즉 null을 가질 수 있다.
세 번째, 기본 타입이 박싱된 기본 타입보다 시간과 메모리 사용면에서 더 효율적이다.
Comparator<Integer> naturalOrder = (i ,j) -> (i < j) ? -1 : (i == j ? 0 : 1);
위의 코드에 (new Integer(42), new Integer(42))
의 값을 출력해 보자.
두 Integer 인스턴스의 값이 42로 같으므로 0을 출력해야 하지만, 실제로는 1을 출력한다.
위의 코드에서 i와 j가 참조하는 오토박싱된 Integer 인스턴스는 기본값으로 변환된다.
그런 다음 첫 번째 정숫값이 두 번째 값보다 작은지를 평가한다.
만약 작지 않다면 두 번째 검사가 이뤄진다.
그런데 이 두 번째 검사에서는 두 객체 참조의 식별성을 검사하게 된다. ( == )
i와 j 가 서로 다른 Integer 인스턴스라면 이 비교의 결과는 false가 되고, 비교자는 1을 반환한다.
이를 통해 박싱된 기본 타입에 == 연산자를 사용하면 오류가 일어난다는 것을 알 수 있다.
실무에서 이와 같이 기본 타입을 다루는 비교자가 필요하다면 Comparator.naturalOrder을 사용하자.
비교자를 직접 만들려면 비교자 생성 메서드나 기본 타입을 받는 정적 compare 메서드를 사용해야 한다.
그리고 박싱된 기본 타입을 사용할 때 주의할 점 하나 더는 거의 예외 없이 기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다는 것이다.
그리고 이때 null 참조를 언박싱하면 NullPointerException이 발생한다.
그럼 박싱된 기본 타입은 언제 사용해야 할까?
컬렉션의 원소, 키, 값으로 쓰일 때 사용한다.
컬렉션은 기본 타입을 담을 수 없으므로 어쩔 수 없이 박싱된 기본 타입을 써야만 한다.
더 일반화하면 매개변수화 타입이나 매개변수화 메서드의 타입 매개변수로는 박싱된 기본 타입을 써야 한다.