본문은 Effective Java를 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
선결론
- 되도록이면 박싱된 기본 타입 대신 기본 타입을 사용하라
- 기본 타입은 박싱된 기본 타입에 비해 안전하고, 간단하고, 빠르다
기본 타입과 박싱된 기본 타입의 주된 차이
- 기본 타입은 값만 가지고 있으나, 박싱된 기본 타입은 값에 더해 식별성(identity)이란 속성을 갖는다
- 기본 타입의 값은 유효하나, 박싱된 기본 타입은 유효하지 않은 값(null)을 가질 수 있다
- 기본 타입이 박싱된 기본 타입보다 시간과 메모리 사용면에서 더 효율적이다
박싱된 기본 타입의 위험성
1) 식별성, 즉 참조값을 비교하므로 예상 밖의 비교 결과가 나올 수 있다
잘못된 구현된 비교자 예제를 통해 박싱된 기본 타입의 위험성을 알 수 있다
Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
- 주어진 두 수를 비교하여 왼쪽의 수가 크면 양수, 같다면 0, 오른쪽의 수가 크면 음수를 반환한다
- 즉, compare의 역할을 하는 메서드이다
해당 비교자를 이용하여 실제 검사를 수행하면 다음과 같이 예상 밖의 결과가 나온다
public class Item61 {
public static void main(String[] args) {
Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
System.out.println(naturalOrder.compare(new Integer(42), new Integer(42)));
// 출력결과: 1
}
}
- 두 Interger의 값이 42로 같으므로 0을 출력해야 할 것 같지만, 실제로는 1을 출력한다
- 이는 두 번째 검사(i==j)가 이뤄지는 과정에서 두 객체의 참조값을 검사하게 되고, 참조값이 다르므로 1을 반환하는 것이다
- 이처럼 박싱된 기본 타입에 == 연산자를 사용하면 오류가 일어날 수 있다
이러한 위험성 때문에 IDE에서 제안을 해준다
- 첫 번째 제안: 비교 연산자 대신 Integer.compare를 사용할 것
- 두 번째 제안: compare를 사용할 때 박싱된 기본 타입 대신 기본 타입을 사용할 것
이 중 첫 번째 제안을 받아들여 메서드 참조로 코드를 변경하면 원했던 결과가 나온다
Integer.compareTo는 Integer.compare를 호출하는데, compare의 매개변수의 타입이 Int이므로 언박싱이 일어나게 된다
기본 타입끼리 비교가 일어나므로 정상적으로 0을 반환한다
비교자에 대한 추가적인 팁
- 기본 타입을 다루는 비교자가 필요하다면 Comparator.naturalOrder()를 사용하라
- 실제로 위 예제에서 메서드 참조 대신 Comparator.naturalOrder()를 넣어도 올바르고 작동한다!
- 비교자를 직접 만들면 비교자 생성 메서드나 기본 타입을 받는 정적 compare 메서드를 사용해야 한다(아이템 14). 이때, Interger 매개변수의 값을 기본 타입 정수로 저장하거나 파라미터로 넘긴 후, 모든 비교를 이 기본 타입 변수로 수행한다
2) NullPointerException이 발생할 수 있다
- 박싱된 기본 타입 변수의 초기값은 null이다
- 이와 같이 초기화를 시키지 않은 상태로 기본 타입 변수와 비교(==)를 시도하면 오토언박싱이 일어나고 NullPointerException이 발생한다
- 기본 타입과 박싱된 기본 타입이 혼용된 연산에서는 오토언박싱과 NullPointerException이 발생할 수 있음을 주의해야 한다
3) 성능이 저하될 수 있다
- 해당 내용은 이펙티브 자바 곳곳에서 나왔던 내용이다
- 박싱과 언방식이 반복해서 일어나면 눈에 띌 정도로 성능이 저하될 수 있다
박싱된 기본 타입을 사용하는 상황
1) 컬렉션의 원소, 키, 값
- 컬렉션은 기본 타입을 담을 수 없으므로
2) 매개변수화 타입이나 매개변수화 메서드의 타입 매개변수(아이템26)
- 자바 언어가 타입 매개변수로 기본 타입을 지원하기 않기 때문에
- 예) ThreadLocal<Integer>
3) 리플렉션(아이템65)를 통해 메서드를 호출할 때
'책 > Effective Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 63: 문자열 연결은 느리니 주의하라 (0) | 2022.07.12 |
---|---|
[이펙티브 자바] 아이템 62: 다른 작업이 적절하다면 문자열 사용을 피하라 (0) | 2022.07.07 |
[이펙티브 자바] 아이템 60: 정확한 답이 필요하다면 float와 double은 피하라 (0) | 2022.07.01 |
[이펙티브 자바] 아이템 59: 라이브러리를 익히고 사용하라 (0) | 2022.07.01 |
[이펙티브 자바] 아이템 58: 전통적인 for문보다는 for-each문을 사용하라 (0) | 2022.06.30 |