본문은 Effective Kotlin을 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
1. nullable 타입 처리하기
nullable 타입은 크게 다음과 세 가지 방법으로 처리할 수 있다.
1) Safe Call 연산자(?.), 스마트 캐스팅, Elvis 연산자 등을 활용한 처리
val printer: Printer? = getPrinter()
printer?.print() // safe call operator
if (printer != null) printer.print() // 스마트 캐스팅
printer ?: return // Elvis operator
2) 리팩토링을 통해 nullable 타입이 나오지 않도록 하기
// Before
val platformType = javaClass().value?
platformType?.doSomething()
// After
val platformType = javaClass().value // 코드 분석 후 메서드 @NotNull 추가
platformType.doSomething()
3) 예외 throw하기
val printer: Printer? = getPrinter()
printer!!.print() // not-null assertion
printer?.print() ?: throw IllegalStatementException() // throwing exception
앞서 아이템7에서 결과 부족이 발생한 경우 예외를 던지는 것은 지양하라고 했으나, 필요에 의해 예외를 throw하는 경우도 있을 수 있다.
또한, not-null assertion(!!)은 너무나도 편리한 기능이지만 다음과 같은 이유로 사용을 지양해야 한다.
- 문제에 대한 충분한 정보를 제공하지 않는다
- 현재는 논리적으로 무조건 not null이라고 하더라도 시간이 흘러 nullable이 될 수 있다
2. 의미없는 nullalblity 사용을 지양하자
다음과 같은 이유로 nullablity 사용을 지양해야 한다.
- null은 값이 부족하다(lack of vaue)라는 것을 나타내므로 이러한 의미가 없을 때는 null을 사용해서는 안 된다
- 앞서 본 것처럼 nullablity를 처리하기 위해선 비용이 발생한다
책에선 nullablity를 피하기 위한 몇 가지 방법을 추천한다.
1) nullable 타입을 제공하는 함수를 추가적으로 제공하기
// 아이템7 참고
fun get(id: Long): Entity
fun getOrNull(id: Long): Entity? // 추가로 제공
2) 클래스 생성 이후 확실히 값이 초기화 된다면 lateinit 프로퍼티와 notNull 델리게이트 사용하기
// 1. lateinit
class UserController {
// 초기화 전에 값을 사용하려고 하면 Exception
// 객체가 동작하기 전에 Exception이 발생하므로 Good (에러는 가능한 한 빨리 발생하는 것이 좋다)
@Autowired
private lateinit var userService: UserService
// ...
}
// 2. notNull 델리게이트
class DoctorActivity: Activity() {
// 초기화 시 null이 들어가면 Exception
// lateinit과 동일하게 초기화 전에 값을 사용하려고 하면 Exception
private var doctorId: Int by Delegates.notNull()
private var fromNotification: Boolean by Delegates.notNull()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
doctorId = intent.extras.getInt(DOCTOR_ID_ARG)
fromNotification = intent.extras.getBoolean(FROM_NOTIFICATION_ARG)
}
}
- lateinit은 참조 타입에 대해서만 사용 가능한 반면, notNull 델리게이트는 참조 타입과 원시 타입 모두에 대해서 사용이 가능하다
- + lateinit과 notNull 델리게이트의 차이점에 대해 구체적으로 알아보고 싶다면 여기를 참고하자
- lateinit은 nullable과 비교해서 다음과 같은 점에서 좋다
- !! 연산자로 언팩(unpack)하지 않아도 된다
- 추후 nullable로 변경할 수 있다
- 초기화가 되어 있음을 보장할 수 있다
3) 요소가 없다면 null 대신 빈 컬렉션 리턴하기
// Bad
fun getList(): List<Entity>? // List<Entity?>와 List<Entity>?는 매우 다르다.
// Good
fun getList(): List<Entity>
4) 존재하지 않는 상수라면 null enum 대신 None enum을 사용하기
Constant? // nullable enum
Constant.None // None enum
'책 > Effective Kotlin' 카테고리의 다른 글
[이펙티브 코틀린] 아이템16: 프로퍼티는 동작이 아니라 상태를 나타내야 한다 (0) | 2023.07.21 |
---|---|
[이펙티브 코틀린] 아이템9: use를 사용하여 리소스를 닫아라 (0) | 2023.07.04 |
[이펙티브 코틀린] 아이템7: 결과 부족이 발생할 경우 null과 Failure를 사용하라 (0) | 2023.07.04 |
[이펙티브 코틀린] 아이템5: 예외를 활용해 코드에 제한을 걸어라 (0) | 2023.07.03 |
[이펙티브 코틀린] 아이템4: inferred 타입으로 리턴하지 말자 (0) | 2023.07.02 |