본문은 Effective Kotlin을 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
1. 제한의 의미와 장점
반드시 특정 흐름으로 코드가 동작해야 한다면 예외를 활용해 제한을 걸어줄 수 있다.
제한을 걸면 다음과 같은 장점이 있다.
- 제약사항을 쉽게 확인할 수 있다
- 예상치 못한 동작을 하기에 앞서 예외를 throw한다 (= 안전한 애플리케이션이 된다)
- 코드를 잘못 쓰는 상황을 막을 수 있다
- 스마트 캐스트 기능을 활용할 수 있다
2. 아규먼트
require 블록을 사용하면 아규먼트를 제한할 수 있다.
fun factorial(n: Int): Long {
require(n >= 0)
return if (n <= 1) 1 else factorial(n - 1) * n
}
fun findClusters(points: List<Point>): List<Cluster> {
require(points.isNotEmpty())
// ...
}
fun sendEmail(user: User, message: String) {
requireNotNull(user.email)
require(isValidEmail(user.email))
// ...
}
// 람다를 활용한 지연 메시지
fun factorial(n: Int): Long {
require(n >= 0) { "Cannot calculate factorial of $n because it is smaller than 0" }
return if (n <= 1) 1 else factorial(n - 1) * n
}
- require 함수는 조건을 만족하지 못할 때 무조건적으로 IllegalArgumentException을 발생시킨다
- require 함수에 람다를 넘겨서 지연 메시지를 사용할 수 있다
3. 상태
check 블록을 이용하면 상태를 제한할 수 있다.
fun speak(text: String) {
check(isInitialized)
// ...
}
fun getUserInfo(): UserInfo {
checkNotNull(token)
// ...
}
fun next(): T {
check(isOpen)
// ...
}
- check 함수는 조건을 만족하지 못할 때 무조건적으로 IllegalStateException을 발생시킨다
4. Assert 계열 함수 사용
assert 블록은 테스트모드에서만 작동하며 실행 과정에서 true 여부를 파악할 수 있다.
fun pop(num: Int = 1): List<T> {
// ...
assert(ret.size == num)
return ret
}
assert는 위 두 함수보다 쓸 일이 거의 없을 듯 하다. 예외를 던지는 것도 아니고 테스트성 코드가 운영코드에 섞여버린다. 차라리 위 두 함수를 쓰는 게 낫다고 본다.
5. nullablity와 스마트 캐스팅
1) 스마트 캐스팅
require와 check 블록으로 어떤 조건을 확인해서 true가 나왔다면 해당 조건은 이후로도 true로 가정한다.
따라서 이를 활용하여 타입 비교를 하면 스마트 캐스트가 작동한다.
이는 nullablity를 체크할 때 유용하다.
fun changeDress(person: Person) {
require(person.outfit is Dress)
val dress: Dress = person.outfit
// ...
}
fun sendEmail(person: Person, message: String) {
require(person.email != null)
val email: String = person.email
}
// notNull을 체크할 땐 requireNotNull, checkNotNull을 사용할 수 있다.
fun sendEmail(person: Person, text: String) {
val email = requireNotNull(person.email)
validateEmail(email)
}
fun sendEmail(person: Person, text: String) {
checkNotNull(person.email)
validateEmail(person.email)
// ...
}
2) Elvis 연산자 이용해서 nullablity 처리하기
Elvis 연산자 오른쪽에 throw나 return을 두고 Elvis 연산자를 활용할 수 있다.
fun sendEmail(person: Person, text: String) {
val email: String = person.email ?: return
}
// 복잡한 처리를 사용할 땐 run 함수를 사용할 수 있다.
fun sendEmail(person: Person, text: String) {
val email: String = person.email ?: run {
log("Email not sent, no email address")
return
}
// ...
}
'책 > Effective Kotlin' 카테고리의 다른 글
[이펙티브 코틀린] 아이템8: 적절하게 null을 처리하라 (0) | 2023.07.04 |
---|---|
[이펙티브 코틀린] 아이템7: 결과 부족이 발생할 경우 null과 Failure를 사용하라 (0) | 2023.07.04 |
[이펙티브 코틀린] 아이템4: inferred 타입으로 리턴하지 말자 (0) | 2023.07.02 |
[이펙티브 코틀린] 아이템3: 최대한 플랫폼 타입을 사용하지 말라 (0) | 2023.07.01 |
[이펙티브 코틀린] 아이템2: 변수의 스코프를 최소화하라 (0) | 2023.06.30 |