질문에 대답하기에 앞서, 용어를 박싱과 언박싱으로 구분하고 각 용어 앞에 Auto-접두사를 붙여 세세하게 나누어 알아보자.
Boxing : Primitive type(기본자료형)을 Wrapper Class로 변환하는 행위.
Unboxing : 박싱과 반대로 래퍼클래스를 기본자료형으로 변환하는 행위.
Auto- : 박싱 혹은 언박싱을 사용자의 별도의 지시가 없어도 자동으로 해주는 기법. Auto Boxing, Auto Unboxing 으로 불린다.
위의 개념을 간단한 코드를 통해 정리해봅시다.
public class Main {
public static void main(String[] args) {
int defaultIntValue = 22;
// 1. Boxing
Integer integer = new Integer(defaultIntValue);
// 2. Unboxing
int unboxingIntValue = integer.intValue();
// 3. Auto Boxing(두 객체의 타입이 다르지만 자유롭게 변경이 가능하다.)
integer = unboxingIntValue;
// 4. Auto Unboxing(두 객체의 타입이 다르지만 자유롭게 변경이 가능하다.)
unboxingIntValue = integer;
}
}
1번은 기본 데이터 타입인 defaultIntValue을 래퍼 클래스인 Integer 객체로 변경했다. 2번은 반대로 래퍼 클래스를 기본자료형으로 변경했다.
3번과 4번이 조금 독특한데, 개발자가 별도의 메서드를 호출하거나, 생성자를 활용하지 않아도 자연스럽게 데이터의 변경이 이뤄지며 심지어 컴파일 오류조차 나지 않는다(!!!) 이것이 오토박싱, 오토언박싱의 특징이다. 특정 조건을 만족하면 개발자가 별도의 작업을 하지 않아도 유연하게 타입을 변경해준다.
'만약 래퍼 클래스가 Null을 언박싱하려고 한다면 어떻게 될까?'
아마 논리적으로 두 가지 방법이 있을 것 이다. 첫 번째로는 캐스팅 중 오류를 발생하는 방법이다. Null은 객체자료형에 사용되고 기본자료형에게 적용할 수 없기 때문이다. 두 번째는 기본자료형의 기본값에 해당하게 값을 변경하는 것이다. 우선 기본자료형 변수에 값을 할당하지 않고 호출해보자. 어떤 값이 나올지 아래 코드를 보기 전에 먼저 생각해보면 좋겠다.
public class Main {
public static void main(String[] args) {
System.out.println("What is default value for an int? :: " + new PrimitiveValue().getIntValue()); // 0
System.out.println("What is default value for a boolean? :: " + new PrimitiveValue().isBooleanValue()); // false
System.out.println("What is default value for a char? :: " + new PrimitiveValue().getCharValue()); // []
}
}
// 기본자료형의 값을 할당하지 않는다면??
class PrimitiveValue{
private int intValue;
private boolean booleanValue;
private char charValue;
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public boolean isBooleanValue() {
return booleanValue;
}
public void setBooleanValue(boolean booleanValue) {
this.booleanValue = booleanValue;
}
public char getCharValue() {
return charValue;
}
public void setCharValue(char charValue) {
this.charValue = charValue;
}
}
필드의 값이 없어 오류가 날 것 같지만 정상적으로 값을 출력한다. 그렇다면 래퍼클래스가 Null이라면 기본자료형의 기본값으로 변경해주지 않을까 내심 기대가 된다. 아래 코드를 보자.
public class Main {
public static void main(String[] args) {
int defaultIntValue = 22;
// Null 객체는 어떻게 오토언박싱이 될까?
Integer integer = null;
int resultIntValue = integer; // NPE 발생
}
}
위 코드는 NullPointerException이 발생한다. 위에서 세운 두 가지 가정 중 아마도 1번을 따르는 것 같다. 자동으로 0으로 변경해주면 좋을 것 같은데 말이다.
그럼 왜 NPE이 발생하는지 오토 언박싱의 과정을 살펴보면서 이해해보자.
... 생략
int resultIntValue = integer; // NPE 발생
... 생략
- 컴파일러는 integer 변수의 타입을 확인한다.(Integer 클래스)
- 데이터를 담으려는 변수 resultIntValue의 타입을 확인한다.(int 타입)
- 자동으로 언박싱을 하기 위해 integer 변수의 intValue() 메서드를 호출한다.(Null 객체의 경우 여기서 NPE가 발생)
- int형으로 변경된 값을 resultIntValue 변수에 담는다.
이제는 명확해졌다. 어느 시점에 NPE가 발생하는지 말이다. 자바는 기본적으로 참조변수타입에 NPE를 발생시킨다. 따라서 본 질문의 답은 '아니오' 이다. 하지만 위와 같이 오토 언박싱의 경우 기본자료형으로 변경하다 발생하는 NPE이므로 특별한 상황에서는 기본자료형에 접근하는 NPE가 발생할 수 있다.
사실 질문의 의도가 변역의 문제인지, 자신의 스킬부족 문제인지 몰라도 명확하게 드러나지 않는다. 그러나 질문 그 자체보다 박싱과 언박싱이 어떤 과정으로 발생하는지, 상황에 따른 결과를 예측할 수 있는지 점검해 볼 수 있어서 좋은 기회였다.