Autoboxing

mongBrown·2026년 4월 21일

Java autoboxing — int 와 Integer 사이에서 일어나는 일

List<Integer> list = new ArrayList<>();
list.add(42);

int value = list.get(0);

42int 고, List<Integer>Integer 만 받는다. 그런데 에러가 나지 않는다. list.get(0)Integer 를 돌려주는데, int 변수에 그냥 담긴다. 이 사이에서 무슨 일이 일어나는 걸까.


List 는 왜 안 되는가

Java 제네릭은 런타임에 타입 정보가 지워지면서 내부적으로 전부 Object 로 처리된다. List<Integer> 는 런타임에 그냥 List 가 되고, 항목 하나하나가 Object 로 저장된다.

문제는 int, long 같은 원시 타입이 Object 를 상속하지 않는다는 것이다. Object 자리에 int 를 넣을 수 없으니 List<int> 는 아예 문법 오류가 난다.

그래서 원시 타입을 컬렉션에 담으려면 각 타입에 대응하는 래퍼 타입(wrapper)을 써야 한다 — intInteger, longLong.

컴파일러가 자동으로 해주는 변환

그런데 매번 list.add(Integer.valueOf(42)) 처럼 직접 감쌀 필요가 없다. 컴파일러가 원시 타입이 객체 자리에 쓰이는 걸 감지하면 자동으로 변환 코드를 삽입한다.

list.add(42);
// 컴파일러가 변환 → list.add(Integer.valueOf(42))

이처럼 intInteger 로 자동으로 포장되는 것을 autoboxing 이라고 한다.

반대 방향도 마찬가지다. Integerint 변수에 담으면 컴파일러가 자동으로 꺼내준다.

int value = list.get(0);
// 컴파일러가 변환 → list.get(0).intValue()

Integerint 로 자동으로 풀리는 것을 unboxing 이라고 한다.

autoboxing  — int      → Integer   (Integer.valueOf())
unboxing    — Integer  → int       (intValue())

null 을 unboxing 하면 어떻게 되는가

Integer 는 참조형이라 null 을 담을 수 있다. 그런데 이걸 int 로 unboxing 하면 문제가 생긴다.

Integer i = null;
int value = i;  // null.intValue() → NullPointerException

컴파일러가 i.intValue() 로 변환하는데, inull 이니 NPE 가 발생한다. intnull 을 담을 그릇이 없기 때문이다.

실무에서는 DB 조회 결과가 null 로 올 수 있는 컬럼을 int 로 받을 때 이 NPE 를 자주 마주친다. Integer 로 받아서 null 여부를 먼저 확인하거나, int 기본값을 지정하는 방식으로 방어해야 한다.

그래서 autoboxing 은 언제 조심해야 하는가

autoboxing 과 unboxing 은 편리하지만 보이지 않는 곳에서 동작하기 때문에 두 가지를 주의해야 한다.

첫째, null 이 올 수 있는 Integerint 로 받으면 NPE 가 난다. Integer 로 받아서 직접 null 체크를 해야 한다.

둘째, 반복문 안에서 래퍼 타입으로 연산하면 autoboxing 이 반복되면서 불필요한 객체가 매 반복마다 만들어진다.

Long sum = 0L;
for (long i = 0; i < 1_000_000; i++) {
    sum += i;
}

sum += i 는 사실 이렇게 동작한다.

1. sum.longValue()          — Long → long  (unboxing, 값을 꺼냄)
2. sum.longValue() + i      — long + long  (덧셈)
3. Long.valueOf(결과값)     — long → Long  (autoboxing, 새 Long 객체 생성)
4. sum = 새 Long 객체       — 참조 교체

3번에서 매 반복마다 새 Long 객체가 Heap 에 만들어진다. 이전 sum 이 가리키던 Long 객체는 참조가 끊기면서 GC 대상이 된다. 루프가 100만 번 돌면 Long 객체가 100만 개 생성됐다가 버려지는 셈이다.

long sum = 0L;  // primitive 로 선언
for (long i = 0; i < 1_000_000; i++) {
    sum += i;   // long + long, 객체 생성 없음
}

sumlong 으로 선언하면 스택에 값만 올라가고 Heap 에 객체가 전혀 생기지 않는다. autoboxing 은 편리하지만 보이지 않는 곳에서 객체를 만들고 있다는 걸 염두에 두고, 반복 연산이 많은 구간에서는 원시 타입으로 선언할 수 있는지 먼저 확인하는 습관이 필요하다.

profile
화이팅!

0개의 댓글