PlayTime
public class PlayTime {
private Integer playTime;
public PlayTime(Integer playTime) {
if (playTime < MIN_PLAY_TIME) {
...
boxing 타입으로 받는 이유가 있을까요?
기본 타입과 박싱된 기본 타입은 차이가 있다.
- 박싱된 기본 타입은 값에 더해 식별성을 가진다. 그래서 박싱된 기본 타입의 두 인스턴스는 값이 같아도 서로 다르다고 식별될 수 있다.
위의 코드는 통과가 된다. 왜냐하면 Integer
클래스에서 내부적으로 valueOf
로 정적 팩토리 메서드를 호출하면 -128 ~ 127까지 캐싱되어 있기 때문이다.
따라서 캐싱 범위를 넘어서거나 new
로 생성할 경우 주소 값이 달라져서 통과하지 못한다.
- 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 값, 즉 null을 가질 수 있다.
sum을 박싱된 기본 타입으로 선언하여 느려졌다. 박싱과 언박싱이 반복되어 느려진다
- 저장 공간 원시타입은 스택영역에, 래퍼클래스는 힙 영역에 저장된다. 따라서 배열등에서 지역성을 이용한 캐시 히트를 높이려면 원시타입이 낫다.
잘못 구현된 비교자
Comparator<Integer> naturalOrder =
(i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
naturlOder.compare(new Integer(42), new Integer(42))
의 값을 출력해보면 0을 출력해야하지만 1을 출력한다.
이유는 첫 번째 검사는 잘 통과하는데 i==j
에서 식병성을 검사하게되어 false가 나오기 때문이다. 박싱된 기본 타입에 ==
연산자를 사용하면 오류가 일어난다.
수정된 비교자
Comparator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
int i = iBoxed, j = jBoxed;
return i < j ? -1 : (i == j ? 0 : 1);
};
public class PlayTime {
private final Integer playTime;
public PlayTime(Integer playTime) {
validate(playTime);
this.playTime = playTime;
}
private void validate(Integer playTime) {
if (playTime < 0) {
throw new IllegalArgumentException("0이상이어야함");
}
}
}
playTime < 0
를 검사할 때 NullPointerException
을 던진다. 초기값이 null
이기 때문이다.
기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다. 그리고 null 참조를 언박싱하면 NullPointerException
이 발생한다.
-
컬렉션의 원소, 키 값. 매개변수화 타입 이나 매개변수화 메서드의 타입 매개변수.
-
리플렉션(아이템 65)를 통해 메서드를 호출할 때도.
-
db와 연동할 때. null 값 처리가 용이하기 때문에 SQL과 연동할 경우에 처리를 원활하게 할 수 있다.
We recommend that you declare consistently-named identifier attributes on persistent classes and that you use a nullable (i.e., non-primitive) type
- Hibernate ORM docs