티스토리 뷰

Integer나 Double 클래스가 가끔 코드에 등장한다. 이들은 기본 타입인 int나 double에 대응하는 래핑(wrapping) 클래스 타입이다. 이런 타입을 많이 접하는 것이 ArrayList<Integer>와 같이 콜렉션 객체를 선언할 때이다. 이 클래스들이 왜 필요할까? 앞의 포스트에서 값 변수와 참조 변수에 대해 살펴보았다. 자바에서 변수는 값 모델(즉 변수가 직접 값을 가지는 형태)과 참조 모델(변수는 객체의 참조를 가짐. 객체는 힙 메모리의 어딘가에 존재한다)로 나눈다. int와 double은 기본타입으로 값모델을 가지는데, 그 경우 int n;이라고 선언하면 n이 나타내는 주소(위치)에 int 값이 저장된다. 이 때 n은 값을 나타내기도 하고 저장소를 나타내기도 한다. 이것을 값모델이라고 한다.

반면 String str = ...; 로 선언된 str 변수는 스트링 객체의 참조를 가진다. 이 경우 str이라는 이름은 참조를 가지고 어딘가 메모리에 저장된 객체를 가리킨다. (C 와 달리 주소가 아니어서 참조라고 한다)

자바에서는 메소드의 매개변수나 라이브러리에서 받는 값이 참조만 가능한 경우가 있다. ArrayList가 그런 대표적인 예인데, 여기에는 원소로는 객체의 참조만 추가될 수 있다. 기본 타입은 객체가 아니므로 기본타입의 값을 넣으려면 대응하는 래핑타입의 객체로 바꾸어 그 참조를 넣게 된다. 이렇게 기본 타입의 값을 객체가 필요한 곳에 사용하기 위해 대응하는 래핑클래스의 객체로 바꾸는 것을 오토박싱이라고 한다. 그래서 int n;으로 선언된 변수 n을 ArrayList에 넣기 위해 원래는(자바 5 이전버전) nList.add(new Integer(n)); 과 같이 직접 Integer 객체를 생성해야 하는데, 자바 컴파일러가 자동으로 이러한 변환 과정을 해주므로 우리는 nList.add(n); 과 같이 쓸 수 있다. 필요할 때 컴파일러에 의해 int 값이 Integer 객체로 바뀌는 이 과정을 오토박싱이라고 한다.

반대의 과정은 오토 언박싱이라고 한다. 래핑클래스 객체가 기본타입이 필요한 자리에 사용될 때 자동으로 기본타입의 값으로 바뀌는 과정이다. 다음 코드는 ArrayList에서 i번째 Integer 객체를 꺼내서 다시 int 타입의 변수에 넣는데, 이때 오토 언박싱이 발생한다.

int n = nList.get(i);

오토박싱과 오토언박싱은 언제든 기본타입의 값이나 변수를 객체 타입이 필요한 자리에 사용할 때마다 일어난다. 우리가 알지 못하는 사이에 생각보다 많은 오토박싱이 일어난다. 

Integer nObj = otherObj + 1;

이런 코드에서 발생하는 일은 다음과 같다.

  • 먼저 otherObj가 int로 오토언박싱된다.
  • 1을 더한 int 값이 객체로 오토박싱되고 그것을  nObj가 참조한다.

Integer와 같은 래핑클래스 타입의 객체는 연산을 할 수 없고 그 자체에서 값이 바뀌지 않는 immutable 객체다. 그러므로 기본타입으로 바꾸어 연산을 수행한 후 그 결과는 새로운 객체가 되어 좌변의 변수에 지정된다. 

후증가 연산 ++의 결과는 더 복잡하다. 즉 nObj++; 연산은 그 자체로 오토언박싱과 오토박싱이 발생한다. 즉 nObj = nObj + 1; 과 같이 계산되므로 nObj는 새로 생성된 다른 객체를 가리키게 된다. 이것을 다음과 같이 확인해 볼 수 있다.

Integer nObj = 5;
System.out.println(nObj + ", " + java.lang.System.identityHashCode(nObj));
nObj++;
System.out.println(nObj + ", " + java.lang.System.identityHashCode(nObj));

결과는 다음과 같다.

5, 460141958
6, 1163157884

즉 nObj는 ++ 이전과 다른 객체가 된다.

그런데 Immutable, 즉 불변객체의 성질에 의해 의외의 일이 일어나는데, 같은 값을 가지는 Integer 객체는 같은 해시코드를 가진다. 즉 하나의 값에 대해 하나의 객체만 존재한다. 

Integer nObj = 5;
System.out.println(nObj + ", " + java.lang.System.identityHashCode(nObj));
nObj *= 2;
System.out.println(nObj + ", " + java.lang.System.identityHashCode(nObj));
Integer nObj2 = 10;
System.out.println(nObj + ", " + java.lang.System.identityHashCode(nObj2));

위의 코드를 수행한 결과는 다음과 같다. 같은 값을 참조하는 객체가 또 생기면 내부적으로 이미 있는 객체들을 찾아보고 그 객체의 참조를 돌려준다는 것이다. 

5, 804564176
10, 245257410
10, 245257410

Number 클래스는 Integer, Double, Float, Long 등의 클래스의 공통 슈퍼 클래스다. Number 클래스는 추상 클래스로 그 자체는 객체를 생성할 수 없다. 또한 Number를 상속한 이런 래핑 클래스들은 final이어서 더이상 상속될 수 없고 이들 숫자 클래스는 모두 Integer의 불변성과 연산의 박싱/언박싱의 특성을 공유한다.

요약하면 기본 타입의 값이나 변수가 객체가 필요한 곳에 매개변수로 쓰이거나 객체 타입의 변수에 지정될 때 (타입이 맞으면) 오토 박싱이 일어난다. 오토 박싱된 객체는 불변이므로 연산을 수행하면 바뀐 값을 가지는 새로운 객체가 생긴다. 또한 이들 객체는 대응하는 기본 타입이 필요한 자리에 쓰이면  오토언박싱이 일어나 기본 타입의 값으로 바뀐다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함