Preconditions : 사전 조건. 특정한 일이 발생하기 전에, 충족되어야 하는 조건
Kotlin에서는 Preconditions은 특정 조건이 충족되지 않은 경우 예외를 발생하는 함수들을 담아 둔 stdlib 함수들입니다.
대표적으로 require, check, error가 있습니다.
@kotlin.internal.InlineOnly
public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
contract {
returns() implies value
}
if (!value) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
}
}
주어진 조건 (value) 이 false인 경우, lazyMessage로 설정한 값과 함꼐 IllegalArgumentException 을 발생시킵니다.
주로 주어진 값을 검증하는 역할로 사용합니다.
@kotlin.internal.InlineOnly
public inline fun <T : Any> requireNotNull(value: T?, lazyMessage: () -> Any): T {
contract {
returns() implies (value != null)
}
if (value == null) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
} else {
return value
}
}
require과는 다르게 value로 받는 값이 null인 경우 exception을 발생시킵니다.
주로 주어진 값이 유효한지 확인하는 데 사용합니다.
@kotlin.internal.InlineOnly
public inline fun check(value: Boolean, lazyMessage: () -> Any): Unit {
contract {
returns() implies value
}
if (!value) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
}
}
@kotlin.internal.InlineOnly
public inline fun <T : Any> checkNotNull(value: T?, lazyMessage: () -> Any): T {
contract {
returns() implies (value != null)
}
if (value == null) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
} else {
return value
}
}
check는 require, checkNotNull은 requireNotNull과 동일하게 동작하지만 require, requireNotNull 와는 다르게 IllegalStateException 를 발생시킵니다.
@kotlin.internal.InlineOnly
public inline fun error(message: Any): Nothing = throw IllegalStateException(message.toString())
해당 함수는 간편하게 에러를 발생시킬 수 있는 함수입니다.
어떠한 경우에던 IllegalStateException 을 발생시키는 로직을 가지고 있습니다.
코드 자체는 같은 동작을 하는데, 왜 똑같은 방식의 함수를 여러 개를 만들었을까요? 이는 반환하는 Exception에서 힌트를 얻을 수 있습니다.
require의 경우에는 함수의 입력값, 즉 매개변수(Argument)를 검증하는데 주로 사용하며, check의 경우에는 코드 로직 내부의 상태(State)를 검증하는 것이 주 목적입니다.
이런 Preconditions 함수들은 일반적인 비즈니스 로직 뿐만 아니라 테스트 코드에서도 사용할 수 있으며, Compose에서도 조건에 맞지 않는 경우 뷰를 그리지 않는 방식으로 사용할 수 있습니다.
@Composable
fun UserInfoView(
name: String?
) {
val stateFlow = viewModel.stateFlow.collectAsState().value
check(stateFlow?.historyList?.isNullOrEmpty()) { "history list is empty" }
requireNotNull(name) { "Name is not valid" }
}
해당 함수들은 조건에 올바르지 않으면 Exception을 반환합니다. Exception을 잘 처리하지 않는다면, Android 앱 위에서는 앱이 충돌할 수 있습니다.
이런 Preconditions 함수들을 사용하는 경우 try-catch를 이용하여 예외를 처리하거나 runCatching을 이용하여 처리해야 합니다.