[자바] 삼항 연산자에서 Null Pointer Exception이 발생한다고?

skyepodium·2022년 1월 10일
1
post-thumbnail

삼항 연산자에서 Null Pointer Exception이 발생하는 경우에 대해 알아봅시다.

같이 일하시는 동료가 알려주었습니다. (feat junit, nankisu 그분의 닉네임입니다.)

1. 예시

다음 삼항 연산자에서는 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) 삼항연산자

하나씩 알아봅시다.

2. auto Unboxing

1) 정의

자바에는 8개의 원시타입이 있습니다.
int, char, byte, short, long, boolean, double, float

이에 대응하는 클래스타입을 참조타입(wrapper class)라고 부릅니다.
Interger, Character, Byte, Short, Long, Boolean, Double, Float

  • boxing - 원시타입을 참조타입에 넣을 때 발생하며
  • unboxing - 참조타입을 원시타입에 넣을 때 발생합니다.

JDK 1.5 부터는 자동으로 지원되며 이를 auto boxing, auto unboxing이라고 부릅니다.

2) 주의사항

auto boxing, unboxing에는 3가지 주의사항이 있습니다.

1. Null Pointer Exception

참조타입이 null인 경우, 원시타입에 넣으려고 하면 값이 없기 때문에 NPE가 발생합니다.

class Main {
    public static void main(String[] args) {
        Long a = null;
        // null값을 autu unboxing 하는 경우 Null pointer Exception 발생
        long b = a;
    }
}

2. 비교 연산자

== 비교 연산자를 수행하면 참조타입의 경우 주소값 비교가 진행되어 제대로된 결과를 얻지 못할 수 있습니다.

3. 메모리 효율성

auto boxing의 경우 쓸모없는 객체를 생성하기 때문에 메모리 누수로 이어질 수 있습니다.

3. 삼항 연산자

1) down casting

삼항 연산자에서는 대입하는 값에 따라 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;
    }
}

4. 그래서 뭐가 문제인데

1) 확인

다시 문제를 보면 다음과 같습니다.

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;
    }
}

2) 개선

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;
    }
}

5. 정리

사실 이펙티브 자바에 따르면 참조타입을 사용하는 경우는 크게 2가지입니다.
1) java colleciton 사용 경우(List, Map)
2) 정말 어쩔 수 없는 못한 경우

그래서 정리하면

  • 가능하면 대부분의 케이스에서 원시타입 사용
  • 삼항 연산자 사용할때는 참조타입, 원시타입 여부 확인 후 다른 경우, 명시적으로 boxing 후 사용
profile
callmeskye

1개의 댓글

comment-user-thumbnail
2022년 1월 12일

크으 실무에서 나올 수 있을법한 문제네요 머리를 탁 치고 갑니다.

답글 달기