Autoboxing은 기본형(, )과 그 (, ) 사이의 Java 자동 변환입니다. 편리하지만 미묘한 함정이 있습니다 — 성능 오버헤드, 놀라운 동작, 그리고 위험입니다.
intdoubleIntegerDouble==NullPointerExceptionInteger boxed = 42; // autoboxing: int → Integer (Integer.valueOf(42))
int unboxed = boxed; // auto-unboxing: Integer → int (boxed.intValue())
List<Integer> nums = new ArrayList<>();
nums.add(5); // int 5 → Integer 로 autobox (컬렉션은 객체가 필요)
int x = nums.get(0); // Integer → int 로 auto-unbox
컬렉션과 generics가 객체를 요구하므로(List<int>를 가질 수 없음) 자동으로 발생하며, Java가 기본형을 투명하게 박싱합니다.
Integer a = 1000;
Integer b = 1000;
a == b; // ❌ FALSE — 서로 다른 Integer 객체 (참조 비교)
a.equals(b); // ✅ true — 값 비교
// 더 나쁜 함정 — Integer 캐시가 작은 값을 동작하는 것처럼 보이게 함:
Integer c = 100, d = 100;
c == d; // TRUE — Java 는 -128 ~ 127 의 Integer 를 캐싱 (같은 객체)
Integer e = 200, f = 200;
e == f; // FALSE — 캐시 범위 밖 → 서로 다른 객체
이는 교활합니다: Integer에 대한 ==는 작은 값(캐싱됨)에는 동작하지만 큰 값에는 실패합니다 — 테스트에서는 올바른 듯하던 코드가 프로덕션에서 깨집니다. 래퍼 값 비교에는 항상 .equals()를 사용하거나 먼저 기본형으로 언박싱하십시오.
Integer value = null; // 래퍼는 null 이 될 수 있음
int x = value; // 💥 NullPointerException — null 언박싱은 null.intValue() 를 호출
Map<String, Integer> map = new HashMap<>();
int count = map.get("missing"); // 💥 NPE — get() 이 null 반환, 그다음 언박싱 실패
null 래퍼를 언박싱하면 NPE가 발생합니다 — 흔하고 놀라운 크래시로, 특히 null을 반환하는 맵 조회에서 그렇습니다.
// ❌ 핫 루프에서 autoboxing — 수백만 개의 Integer 객체 생성 (GC 압력, 느림)
Long sum = 0L; // 잘못된 타입 — 래퍼
for (long i = 0; i < 1_000_000; i++) {
sum += i; // 언박싱, 더하기, 재박싱 → 매번 새 Long!
}
// ✅ 핫 경로에서는 기본형 사용
long sum = 0L; // 기본형 — 박싱 없음
반복적인 박싱/언박싱은 과도한 객체를 생성하여 빡빡한 루프에서 성능을 해칩니다.
Autoboxing은 편리하고 만연해 있지만(컬렉션, generics가 모두 이에 의존), 그 함정은 실제로 진단하기 어려운 버그를 일으킵니다.
== 함정이 특히 위험합니다: Integer를 ==로 비교하면 캐싱된 작은 값(-128 ~ 127)에는 동작하지만 더 큰 값에는 조용히 실패합니다 — 테스트를 통과하고 프로덕션에서 깨지는 버그 — 따라서 래퍼 비교에는 .equals()(또는 언박싱)가 필수적입니다. null 래퍼 언박싱으로 인한 NullPointerException(맵 조회에서 흔함)은 또 다른 빈번한 크래시입니다.
그리고 핫 루프에서의 박싱은 불필요한 객체를 생성하여 성능을 해칩니다.
박싱이 언제 발생하는지, 값 대 참조 비교 문제, null 언박싱 위험을 이해하고 성능이 중요한 코드에서 기본형을 사용하는 것은 올바르고 효율적인 Java를 작성하는 데 중요합니다 — 그리고 Integer 캐시 == 동작은 깊은 이해를 드러내는 고전적인 면접 질문입니다.