기본형 변수를 다룰 때 부득이하게 기본형을 객체로써 다뤄야하는 경우가 발생합니다. 예를들면 매개변수로 객체만을 받거나, 값을 객체로만 저장해야하는 경우 등과 같은 상황들이 있습니다. 이런 경우에 기본형 변수들을 객체로 변경해서 작업을 수행해야합니다.
이때 기본형 변수들을 객체형으로 만들어주는 클래스가 바로 Wrapper 클래스
입니다. 기본형 값을 객체로 포장하고 있다고해서 이런 이름이 붙었습니다.
Wrapper 클래스
는 8가지 기본형을 객체로 다루게 해주는데, char, int
를 제외하고는 기본형의 첫글자를 대문자로 만들어져있습니다.
기본형 | Wrapper 클래스 |
---|---|
boolean | Boolean |
char | Character |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
이때 기본형을 Wrapper 클래스의 객체로 만드는 것을 박싱(boxing)
이라고 하고 객체로부터 기본형값을 얻는 것을 언박싱(unboxing)
이라고 부릅니다.
이때 한 번 박싱된 객체의 값은 변경할 수 없습니다. 즉, 래퍼 객체는 단순히 기본형을 객체로 만드는 일만 하는 것입니다.
그러면 기본형을 가지고 박싱과 언박싱을 수행해보겠습니다.
public class Main {
public static void main(String[] args) {
Integer integerObj = 10; //박싱
int integerValue = integerObj; //언박싱
System.out.println(integerObj.intValue()); //Integer 객체의 값을 int형으로 반환
System.out.println(integerValue);
}
}
참고로 연산시 래퍼 객체의 언방싱이 자동적으로 이루어집니다.
System.out.println(10 + integerObj); //integerObj 언박싱 후 +연산 수행
Wrapper 클래스
에는 String 타입의 값을 기본형으로 변경하는 메소드도 정의되어 있습니다. 이 메소드는 정적(static) 메소드로 정의되어 있습니다. 이 메소드를 사용해서 문자열을 8가지 기본형값으로 변경할 수 있습니다.
parseXxxxx()
는 문자열을 기본형값으로 변환하고 반환합니다.
public class Main {
public static void main(String[] args) {
String str = "10";
System.out.println(10 + str); //문자열 연결 수행
System.out.println(10 + Integer.parseInt(str)); //덧셈 연산 수행
}
}
+
연산자는 피연산자 중 한 쪽이 문자열이면 문자열을 연결하는 연산을 수행하고, 양쪽이 숫자라면 덧셈 연산을 수행합니다.
위에서 예시로든
intValue()
와 헷갈리시면 안됩니다.
intValue()
는 래퍼 클래스를 언박싱해서 int형 값을 반환하고,parseInt()
는 문자열을 int형값으로 반환하는 메소드입니다.또한
intValue()
는 일반 메소드이므로 Integer 객체를 생성하고 사용해야하고,parseInt()
는 정적 메소드이기 때문에 객체 선언없이 바로 사용할 수 있습니다.
valueOf()
는 문자열을 Wrapper 클래스
로 변환해서 반환하는 메소드입니다.
Boolean bool = Boolean.valueOf("true");
Integer integer = Integer.valueOf("10");
JDK 1.5 버전에서 오토 박싱과 오토 언박싱이 도입되면서 parseXxxxx()와 valueOf() 간의 차이가 없어졌습니다.
사실 위에서 작성한 코드가 손수 박싱을 하는 것이 아닌 오토 박싱과 오토 언박싱을 이용한 코드였습니다.
//박싱
int num = 10;
Integer integerNum = new Integer(num);
//오토 박싱
int num = 10;
Integer integerNum = num;
//언박싱
Integer integerNum = new Integer(10);
int num = integerNum.intValue();
//오토 언박싱
Integer integerNum = new Integer(10);
int num = integerNum;
따라서 어떤 메소드를 사용해도 오토 박싱과 오토 언박싱 덕분에 차이가 나지 않게되는데요. 한 가지 첨언을 하자면 valueOf()
의 속도가 느리기 때문에 parseXxxxx()
를 사용하는 것이 조금 더 좋다고 할 수 있습니다.
더 빠른 성능을 위해서라면 오토 박싱과 오토 언박싱에 맡기기보다는 동일한 타입으로 연산이 되게끔 하는 것이 제일 좋습니다.
Wrapper 객체
의 ==, !=
연산은 값이 아닌 주소를 비교합니다. 결국 객체는 참조형이기 때문이죠.
public class Main {
public static void main(String[] args) {
Integer num1 = 1000;
Integer num2 = 1000;
System.out.println(num1 == num2);
}
}
값은 동일하지만 서로 다른 객체이기 때문에 ==
의 결과로 false
를 반환합니다.
문제는 예외 사항인데요. 내부적으로 boolean, char, byte, short, int
에서 아래 표에 명시된 범위 내의 값들은 같은 객체를 공유합니다.
타입 | 범위 |
---|---|
boolean | true, false |
char | \u0000 ~ \u007f |
byte, short, int | -128 ~ 127 |
public class Main {
public static void main(String[] args) {
Integer num1 = 10;
Integer num2 = 10;
System.out.println(num1 == num2);
System.out.println(num1.equals(num2));
}
}
int의 범위에 속하는 10이라는 값을 가진 두 객체를 ==
와 equals()
로 비교했을 때 결과는 둘 다 true
로 값도 같고 객체도 동일하다라고 판정됩니다.
public class Main {
public static void main(String[] args) {
Integer num1 = 10;
Integer num2 = 10;
System.out.println(num1.hashCode());
System.out.println(num2.hashCode());
}
}
해시 코드가 같아버리면서 완전히 두 객체는 같다고 해버리죠.
이러한 동작 때문에 boolean, char, byte, short, int
값을 사용할 때는 ==. !=
보다는 equals()
로 비교하는 것이 안정적인 결과를 얻을 수 있습니다.
Number 클래스
는 숫자 값을 가지는 Wrapper 클래스
(Byte, Short, Integer, Long, Float, Double)의 조상 클래스입니다.
Number 클래스
는 추상 클래스이기 때문에 숫자를 활용하는 Wrapper 클래스들의 메소드들을 추상 메소드 형태로 가지고 있습니다. 즉, Byte, Short, Integer, Long, Float, Double
들에게 공통된 메소드(xxxxxValue()
와 같은)들을 제공하기 위해 존재하는 클래스라고 할 수 있습니다.
이들 외에도 BigInteger, BigDecimal
같은 클래스도 자식으로 가지고 있는데 BigInteger
는 long의 범위보다 더 큰 정수를 처리하고, BigDecimal
은 double의 범위보다 더 큰 부동소수점수를 처리하기 위해 존재하는 클래스입니다.