예외를 활용해 코드에 제한을 걸어라
fun pop(num: Int = 1): List<T> {
//아규먼트 제한
require(num <= size) {
"Cannot remove more elements than current size"
}
//상태 동작 제한
check(isopen) { "Cannot pop from closed stack" }
val let = collection.take(num)
collection = collection.drop(num)
//true 확인
assert(ret.size == num)
return ret
}
위와 같이 제한을 하면, 다음과 같은 장점이 있다.
input 에 대해 제한을 걸 수 있다. (ex. 숫자는 양의 정수, 이메일 주소 형식 판별)
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
}
또한 IllegalArgumentException
을 사용하므로 람다식을 사용해 메시지를 정의할 수 있다.
구체적인 조건을 만족할 때만 함수를 사용할 수 있게 해야 할 때 사용한다. (ex. 객체가 미리 초기화되어 있어야만 처리, 사용자가 로그인했을 때만 처리)
Require과 유사하지만 IllegalStateException
을 발생한다. 일반적으로 Require - Check의 순서다.
함수 내에서 로직이 참인지 확인할 때 사용한다.
fun pop(num: Int = 1): List<T> {
// ...
assert(ret.size == num) //java.lang.AssertionError: Assertion failed 발생
return ret
}
-ea JVM옵션을 활성해야 확인할 수 있고, 테스트 할 때만 활성화 된다.
단위테스트 AssertThat와 비교해 좋은 점은 다음과 같다.
fun changeDress(person: Person) {
require(person.outfit is Dress)
val dress: Dress = person.outfit
}
require을 통과한 경우, person.outfit이 final이라면 Dress로 스마트 캐스팅된다.
특히 null 체킹에 유용한데, requireNotNull
, checkNotNull
을 사용해 null에 대한 스마트 캐스팅이 가능하다.
elvis 연산자를 이용해 null인 경우 로그를 남기도록 하거나 throw로 예외를 던질 수 있다.
fun sendEmail(person: Person, text: String) {
val email: String = person.email ?: run {
log("Email not sent, no email address")
return
}
// ...
}
잘봤습니다.