삼항 연산자에서 Null Pointer Exception이 발생하는 경우에 대해 알아봅시다.
같이 일하시는 동료가 알려주었습니다. (feat junit, nankisu 그분의 닉네임입니다.)
다음 삼항 연산자에서는 Null Pointer Exception
이 발생합니다.
class Main {
public static void main(String[] args) {
// 1. 참조타입에 null값 대입
Long a = null;
// 2. 삼항 연산자 수행
// 조건이 false 이기 때문에 a가 b에 대입
Long b = false ? 0L : a;
}
}
원인은 2가지입니다.
1) unboxing
2) 삼항연산자
하나씩 알아봅시다.
자바에는 8개의 원시타입이 있습니다.
int, char, byte, short, long, boolean, double, float
이에 대응하는 클래스타입을 참조타입(wrapper class)라고 부릅니다.
Interger, Character, Byte, Short, Long, Boolean, Double, Float
JDK 1.5
부터는 자동으로 지원되며 이를 auto boxing, auto unboxing
이라고 부릅니다.
auto boxing, unboxing에는 3가지 주의사항이 있습니다.
참조타입이 null인 경우, 원시타입에 넣으려고 하면 값이 없기 때문에 NPE
가 발생합니다.
class Main {
public static void main(String[] args) {
Long a = null;
// null값을 autu unboxing 하는 경우 Null pointer Exception 발생
long b = a;
}
}
==
비교 연산자를 수행하면 참조타입의 경우 주소값 비교가 진행되어 제대로된 결과를 얻지 못할 수 있습니다.
auto boxing의 경우 쓸모없는 객체를 생성하기 때문에 메모리 누수로 이어질 수 있습니다.
삼항 연산자에서는 대입하는 값에 따라 down casting이 발생합니다.
대입값에 대해 down casting이 발생하며 원시타입, 참조타입이 있으면 원시타입으로 down casting 됩니다.
class Main {
public static void main(String[] args) {
// 1. a - 원시타입, b - 참조타입
long a = 1;
Long b = 2L;
// 2. 원시타입으로 down casting 발생
// c에 대입할 때 boxing 발생
Long c = false ? a : b;
}
}
다시 문제를 보면 다음과 같습니다.
1) 원시타입, 참조타입 대입 값에 대해 down casting 발생
2) 조건문이 false 이기 때문에 a 대입
3) a는 참조타입이기 때문에 원시타입으로 Unboxing
4) null을 Unboxing 하면 Null Pointer Exception 발생
class Main {
public static void main(String[] args) {
// 1. 참조타입에 null값 대입
Long a = null;
// 2. 삼항 연산자 수행
// 1) 원시타입, 참조타입 대입 값에 대해 down casting 발생
// 2) 조건문이 false 이기 때문에 a 대입
// 3) a는 참조타입이기 때문에 원시타입으로 Unboxing
// 4) null을 Unboxing 하면 Null Pointer Exception 발생
Long b = false ? 0L : a;
}
}
null값을 unboxing 하면 Null Pointer Exception이 발생하기 때문에 명시적으로 boxing 해줍니다.
class Main {
public static void main(String[] args) {
Long a = null;
// 명시적으로 boxing 해줍니다.
Long b = false ? (Long)0L : (Long) a;
}
}
사실 이펙티브 자바에 따르면 참조타입을 사용하는 경우는 크게 2가지입니다.
1) java colleciton 사용 경우(List, Map)
2) 정말 어쩔 수 없는 못한 경우
그래서 정리하면
크으 실무에서 나올 수 있을법한 문제네요 머리를 탁 치고 갑니다.