자바의 기본형(primitive type)을 객체로 감싼(wrap) 클래스.
자바에는 두 종류의 타입이 있다.
int, double, boolean 같은 것. 값 자체만 들고 있고 객체가 아니다. String, 우리가 만든 클래스 등. 객체이다.기본형은 가볍고 빠르지만 객체가 아니라서 못하는 일이 있다. 그래서 자바는 기본형마다 짝이 되는 클래스를 하나씩 만들어 뒀는데, 이게 래퍼 클래스이다.
| 기본형 | 래퍼 클래스 |
|---|---|
| int | Integer |
| long | Long |
| double | Double |
| float | Float |
| boolean | Boolean |
| char | Character |
| byte | Byte |
| short | Short |
List<int> numbers = new ArrayList<>(); // 컴파일 에러
List<Integer> numbers = new ArrayList<>(); // OK
Map<String, Integer> 같은 걸 쓰러면 래퍼 타입이 필수.
이게 실무에서 가장 중요한 차이이다.
int count = null; // 불가능. int는 항상 어떤 숫자여야 함
Integer count = null; // 가능. "값이 아직 없다"는 뜻
int는 초기화하지 않으면 0이 된다. 그런데 "조회수가 0회"인 것과 "조회수를 아직 모른다"는 전혀 다른 상황. 래퍼 타입을 쓰면 이 둘을 구분할 수 있다.
int n = Integer.parseInt("123"); // 문자열 → 숫자
Integer.MAX_VALUE; // int의 최댓값
Integer.toBinaryString(10); // "1010"
자바가 둘 사이를 알아서 변환해준다.
Integer boxed = 10; // 오토박싱: int → Integer
int unboxed = boxed; // 언박싱: Integer → int
편하긴 하지만, 이것 때문에 2가지 함정이 생길 수 있다.
Integer count = null;
int result = count + 1; // 💥 NPE 발생
count + 1을 계산하려면 count를 int로 꺼내야 하는데, null은 꺼낼 값이 없어 NPE가 발생한다.
== 비교Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // false (!!)
System.out.println(a.equals(b)); // true
==는 객체를 비교할 때 "같은 값인가"가 아니라 "같은 객체인가"를 본다. a와 b는 값은 같지만 서로 다른 객체라서 false가 나온다.
참고로 -128 ~ 127 범위는 자바가 객체를 재사용(캐싱)하기 때문에 ==가 true로 나온다. 그래서 작은 숫자로 테스트하면 잘 되는 것처럼 보이다가 나중에 터져 버그가 될 수 있다. 래퍼 타입 비교는 무조건 equals()라고 외워두면 편하다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id; // long이 아니라 Long
private String name;
private Integer age; // 나이는 선택 입력이라면 Integer
}
id를 Long으로 쓰는 이유가 바로 null 때문. 아직 DB에 저장되지 않은 엔티티는 id가 없는 상태인데, long이면 0이 들어가서 "id가 0번인 회원"과 구분이 안된다.
정리하면
equals(), 언박시 전엔 null 체크