적절하게 null을 처리하라
String.toIntOrNull()
: String을 Int로 적절하게 변환할 수 없는 경우Iterable<T>.firstOrNull(() -> Boolean)
: 주어진 조건에 맞는 요소가 없을 경우 null위와 같이 null은 최대한 명확한 의미를 갖도록 하는 것이 좋다.
null 의 경우, 아래와 같이 세가지 방법으로 처리할 수 있다.
?.
, 스마트 캐스팅, Elvis 연산자 등을 활용해서 안전하게 처리printer?.print() //safe call
if (printer != null) printer.print() //smart casting
//Elvis 연산자
val printName1 = printer?.name ?: "Unnamed"
val printName2 = printer?.name ?: return
val printName3 = printer?.name ?: throw Error("에러")
위의 방식들은 개발자에게 오류를 알리지 않고, 코드가 진행되는 방법이다. 만약 null일때 문제가 발생한다면 개발자에게 오류를 강제로 발생시켜주는 것이 좋다.
이때 throw
, !!
, requireNotNull
, checkNotNull
을 사용해 강제로 오류를 발생시킬 수 있다.
!!
은 null이 나오지 않는다는 것이 거의 확실한 상황에서 많이 사용된다. 하지만 미래에 null이 가능성은 언제나 존재한다. 따라서 결국 NPE 예외로 이어질 수 있다.
또한 계속해서 언팩(unpack)해야해서 귀찮아질 수 있고, 의미있는 null 값을 가질 가능성을 차단해버리게 된다.
!!
은 웬만하면 지양하는 것이 좋고, lateinit
, Delegates.notNull
을 사용하는 것이 좋다.
의미가 없다면 null을 사용하지 않는 것이 좋다. 이유 없이 사용한다면 !!
을 사용하게 되고, 이는 의미 없이 코드를 더럽히게 된다.
nullability 를 피하는 방법은 다음과 같다.
클래스가 클래스 생성 중 초기화할 수 없는 프로퍼티를 가질 수 있는데, 이는 사용 전 반드시 초기화해서 사용해야 한다. (ex. @BeforeEach
)
lateinit은 처음 사용하기 전 반드시 초기화된다는 것을 의미한다. nullable과 비교해 다음과 같은 차이가 있다.
notNull 델리게이트는 JVM에서 Int, Long, Double, Boolean과 같은 기본 타입과 연결된 타입으로 프로퍼티를 초기화해야 할 때 사용한다. (lateinit 보다 느리다)
private var doctorId: Int by Delegates.notNull()
lateinit, Delegates.notNull: https://codechacha.com/ko/diff-between-deligate-and-lateinit-in-kotlin/
좋은 글 감사합니다!