-
item 61 박싱된 기본 타입보다는 기본 타입을 사용하라책/이펙티브 자바 2022. 4. 21. 17:52
ITEM 61 박싱된 기본 타입보다는 기본 타입을 사용하라
자바의 데이터 타입은 크게 두 가지로 나눌 수 있다
- 기본타입
- int, double, boolean
- 참조타입
- String, List
각각의 기본 타입에는 대응하는 참조타입이 하나씩 있으며 이를 박싱된 기본 타입이라고 한다
- ex) int, double, boolean 에 대응하는 박싱된 기본타입은 Integer, Double, Boolean이다
오토박싱과 오토언박싱 덕분에 두 타입을 크게 구분하지 않고 사용 가능 하지만 그렇다고 차이가 없는 것은 아니다
- 둘 상시에는 분명한 차이가 있으니 어떤 타입을 사용할건지는 중요하며 신중하게 선택 해야 한다
- 기본 타입과 박싱된 기본 타입은 크게 세가지 차이가 있다
- 기본 타입은 값만 가지고 있지만 박싱된 기본 타입은 값에 더해 식별성이란 속성을 가진다
- 박싱된 기본타입의 두 인스턴스는 값이 같아도 서로 다르다고 식별 가능하다는것
- 기본 타입의 값은 언제나 유효하나 박싱된 기본 타입은 유효하지 않은 값, null을 가질 수 있다
- 기본 타입이 박싱된 기본 타입보다 시간과 메모리 사용면에서 더 효율적이다
- 기본 타입은 값만 가지고 있지만 박싱된 기본 타입은 값에 더해 식별성이란 속성을 가진다
- 위의 세가지 차이 때문에 주의해서 사용하지 않으면 문제가 발생 가능 하다
- 박싱된 기본 타입에 == 연산자를 사용하면 오류가 난다
- 기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다
잘못 구현된 비교자 Comparator<Integer> naturalOrder = (i,j) -> (i<j) ? -1 : (i == j ? 0 : 1); 별다른 문제가 없는것 처럼 보이며 원소 개수가 많은 리스트를 넣어서 돌려도 크게 문제가 없다 하지만 naturalOrder.compare(new Integer(42), new Integer(42)); 이 코드를 돌리면 문제가 발생한다 두 Integer 인스턴스의 값이 42로 같기 때문에 0이 출력되어야 하지만 1이 출력된다
naturalOrder의 첫번째 검사(i < j) 는 정상 동작한다
여기서 i와 j가 참조하는 오토박싱된 Integer 인스턴스는 기본 타입 값으로 변환된다
그 후 첫번째 정숫값이 두번째 값보다 작은지 평가한다
작지 않다면 두 번째 검사(i == j)가 이뤄진다
두번째 검사에서는 두 객체참조의 식별성을 검사하게 된다
i와 j가 서로 다른 Integer 인스턴스라면 값은 같더라도 이 연산에서 false가 나오면서 비교자는 잘못된 결과인 1을 반환한다위와 같이 기본 타입을 다루는 비교자가 필요하다면 Comparator.naturalOrder()를 사용하자
문제를 수정한 비교자
ComparatornaturalOrder = (iBoxed, jBoxed) -> {
int i = iBoxed, j = jBoxed; //오토박싱
return i < j ? -1 : (i == j ? 0 : 1);
};
비교자를 직접 만들면 비교자 생성 메서드나 기본 타입을 받는 정적 compare 메서드를 사용해야 한다
그렇더라도 이 문제를 고치려면 지역변수 2개를 두어 각각 박싱된 Integer 매개변수의 값을 기본 타입 정수로 저장하고 모든 비교를 이 기본타입 변수로 수행해야 한다
이러면 식별성 검사가 이뤄지지 않아 오류가 발생하지 않는다기이한 프로그램
public class Unbelievable {
static Integer i;public static void main(String[] args) {
if (i == 42)
System.out.println("믿을수없음");
}
}i == 42 를 검사할때 NullPointException을 던진다
원인은 i가 int가 아닌 Integer이며 다른 참조 타입 필드와 마찬가지로 i의 초기값도 null이라 그렇다
i == 42는 Integer와 int를 비교하는 것이며 박싱된 기본 타입의 박싱이 자동으로 풀리고 null참조를 언박싱하면 NullPointException이 발생한다- i를 int로 선언하면 문제없이 동작한다
엄청나게 느린 코드
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을 박싱된 기본 타입으로 선언하여 느려졌다
오류나 경고 없이 컴파일 되지만 박싱과 언박싱이 반복해서 일어나 체감될 정도로 성능이 느리다박싱된 기본타입은 언제 써야되나? * 컬렉션의 원소, 키, 값으로 사용한다 * 컬렉션은 기본 타입을 담을 수 없어서 어쩔수 없이 박싱된 기본 타입을 써야한다 * 매개변수화 타입이나 매개변수화 메서드의 타입 매개변수로는 박싱된 기본 타입을 써야한다 * 자바는 타입 매개변수로 기본 타입을 지원하지 않는다 * ThreadLocal<int> 이건 안되지만 ThreadLocal<Integer>는 가능하다는 이야기 * 리플렉션을 통해 메서드를 호출하는 경우도 박싱된 기본 타입을 써야한다 #### 기본 타입과 박싱된 기본 타입 중 하나를 선택해야 하면 기본 타입을 사용하자 기본 타입은 간단하며 빠르다 #### 박싱된 기본 타입을 써야 한다면 주의를 기울이자 #### 오토박싱이 박싱된 기본 타입을 사용할 때의 번거로움을 줄여주지만 그 위험까지 없애주지는 않는다 #### 두 박싱된 기본 타입을 == 연산자로 비교한다면 식별성 비교가 이뤄지는데 이는 개발자가 원한게 아닐 가능성이 크다 #### 같은 연산에서 기본 타입과 박싱된 기본 타입을 혼용하면 언박싱이 이뤄지며 언박싱 과정에서 NPE 발생 가능하다 #### 기본 타입을 박싱하는 작업은 필요 없는 객체를 생성하는 부작용을 나을 수 있다
'책 > 이펙티브 자바' 카테고리의 다른 글
item 63 문자열 연결은 느리니 주의하라 (0) 2022.04.21 item 62 다른 타입이 적절하다면 문자열 사용을 피하라 (0) 2022.04.21 item 60 정확한 답이 필요하다면 float와 double은 피하라 (0) 2022.04.21 item 59 라이브러리를 익히고 사용하라 (0) 2022.04.21 item 58 전통적인 for 문보다는 for-each문을 사용하라 (0) 2022.04.21 - 기본타입