기본 타입과 박싱된 기본 타입의 차이
- 기본 타입은 값만 가지나, 박싱된 기본 타입은 식별성이라는 속성을 갖는다.(값이 같아도 서로 다르다고 식별될 수 있다)
- 기본 타입의 값은 언제나 유효하나 박싱된 기본 타입은 null일 수 있다.
- 기본 타입이 박싱된 기본타입보다 시간과 메모리 사용면에서 더 효율적이다.
박싱된 기본타입의 비교자
잘못 구현된 비교자
Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
int result = naturalOrder.compare(new Integer(42), new Integer(42));
위 코드의 result는 값이 같으므로 0을 출력해야하지만 1을 출력한다.
첫 번째 검사(i<j)는 오토박싱된 Integer 인스턴스가 기본 타입 값으로 변환되지만, 두 번째 검사(i==j)는 두 객체 참조의 식별성을 검사한다. 즉, i와 j가 서로 다른 Integer 인스턴스라면 값은 같더라도 참조는 다르기 때문에 false가 되고 i가 더 크다고 잘못 판단하는 것이다.
이처럼 박싱된 기본 타입에 == 연산자를 사용하면 오류가 발생한다.
해결 방법 두 가지
Comparator<Integer> integerComparator = Integer::compare;
Comparator<Integer> naturalOrder = Comparator.naturalOrder();
Comparator.naturalOrder()를 이용하면, 내부적으로 compare 메소드가 Integer의 compareTo 메소드로 연결되고, compareTo 메소드는 다시 자신의 필드인 intValue를 넣는 형태고, integerComparator는 Integer의 값을 int 파라미터로 넘길 때 즉시 오토박싱한다.
방식이 다르니, 둘의 성능차이가 있는지 확인해보려 코드를 짰으나, 매번 유의미한 같은 결과는 나오지 않았다 😭 잘못된 방식이라 그런건진 모르겠지만 제가 잘못된 코드를 작성했다면 코멘트 남겨주시면 감사하겠습니다🙏🏻 [ 테스트코드 ]
박싱된 기본 타입과 기본 타입의 혼용 이슈
기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다.
static Integer i;
public static void main(String[] args) {
if (i == 42)
System.out.println("NPE가 안터짐");
}
그렇다면, 위 코드와 같이 null 참조를 언박싱하면 어떻게될까? NullPointerException이 발생한다.
그래서 이 경우에는 i를 박싱된 기본 타입이 아닌 기본타입인 int를 선언하면 해결된다.
박싱과 언박싱이 반복될 때 성능 저하
오류나 경고 없이 컴파일 되지만 박싱과 언박싱이 반복해서 일어나면 체감될 정도로 성능이 저하된다.
@Test
public void boxingUnboxing() {
Long sum = 0L;
for(long i=0;i<Integer.MAX_VALUE;i++){
sum+=i;
}
}
@Test
public void noUse(){
long sum = 0L;
for(long i=0;i<Integer.MAX_VALUE;i++){
sum+=i;
}
}
위 테스트 코드의 속도를 비교해 본 결과 boxing과 unboxing을 반복적으로 하는 코드가 성능이 낮다는 것을 알 수 있었다.
현재까지 설명했던 모든 문제의 원인은 기본 타입과 박싱된 기본 타입의 차이를 무시한 대가다.
박싱된 기본 타입은 언제 사용하는가
컬렉션은 기본 타입을 담을 수 없어 어쩔 수 없이 박싱된 기본 타입을 써야한다. 매개변수 타입이나 매개변수화 메소드의 타입 매개변수로는 자바 언어가 타입 매개변수로 기본 타입을 지원하지 않기 때문에 박싱된 기본 타입을 써야한다는 뜻이다. 뿐만아니라 리플렉션을 통해 메소드를 호출할 때도 박싱된 기본 타입을 사용해야 한다.
정리
기본 타입은 간단하고 빠르며 때문에 가능한 기본 타입을 사용하는게 좋지만 박싱된 기본 타입을 써야할 때도 있을 것이다.
오토박싱이 기본 타입을 사용할 때 번거로움은 줄여주지만 위험까지 없애지는 않기 때문에 주의해야한다.
위에서 이야기한 식별성 비교에 대한 문제나, 언박싱 과정에서 NPE를 던지거나, 기본 타입을 박싱하는 것은 필요 없는 객체를 생성하는 부작용을 발생시킬 수 있다.
* 위 글은 EffectiveJava 3/E 책 내용을 정리한 글로, 저작권 관련 문제가 있다면 댓글로 남겨주시면 즉각 삭제조치 하겠습니다.
'Reading > Effective Java' 카테고리의 다른 글
[Effective-Java] Item 64, 65.객체는 인터페이스를 사용해 참조하라, 리플렉션보다는 인터페이스를 사용하라 (0) | 2021.12.01 |
---|---|
[Effective-Java] Item 62, 63.다른 타입이 적절하다면 문자열 사용을 피하라, 문자열 연결은 느리니 주의하라 (0) | 2021.11.26 |
[Effective-Java] Item 60.정확한 답이 필요하다면 float와 double은 피하라 (0) | 2021.11.25 |
[Effective-Java] Item 59.라이브러리를 익히고 사용하라 (0) | 2021.11.25 |
[Effective-Java] Item 58.전통적인 for 문보다는 for-each 문을 사용하라 (0) | 2021.11.22 |