- 확실하게 어떤 형태로 동작해야 하는 코드가 있다면 예외를 활용해 제한을 걸어주는 것이 좋은데 코틀린에서는 코드의 동작에 제한을 걸 때 다음과 같은 방법을 사용할 수 있음
- require 블록 : 아규먼트 제한
- check 블록 : 상태와 관련된 동작 제한
- assert 블록 : 어떤 것이 true인지 확인 가능 그러나 테스트 모드에서만 동작
- return 도는 throw와 함께 활용하는 Elvis 연산자
예시
class Stack<T>(
var collection: List<T>
) {
private val size = 3
private val isOpen = false
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 ret = collection.take(num)
collection = collection.drop(num)
assert(ret.size == num)
return ret
}
}
위 코드의 장점
- 제한을 걸면 문서를 읽지 않은 개발자도 문제 확인 가능
- 문제가 있을 경우에 함수가 예상하지 못한 동작을 하지 않고 throw함
- 예상하지 못한 동작을 하는 것보단 throw 하는 것이 훨씬 안정적이고 관리하기 좋음
- 코드가 어느 정도 자체적으로 검사가 됨
- 스마트 캐스트 기능을 활용할 수 있게 되므로, 캐스트를 적게할 수 있음
아규먼트
- 함수 정의할 때 타입 시스템을 활용해서 아규먼트에 제한을 거는 코드를 많이 사용
예시
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())
//...
return points.map { Cluster() }
}
fun sendEmail(user: User, message: String) {
requireNotNull(user.email)
require(isValildEmail(user.email))
}
fun isValildEmail(email: String): Boolean {
// ...
return false
}
- 이와 같은 형태의 입력 유효성 검사 코드는 함수의 가장 앞부분에 배치되므로 읽는 사람도 확인하기 쉬움
- require 함수는 조건을 만족시키지 못할 때 무조건적으로 IllegalArgumentException 을 발생시키므로 제한을 무시할 수 없음
- 추가로 람다를 활용해 메시지 정의할 수 있음
상태
- 어떤 구체적인 조건을 만족할 때만 함수를 사용할 수 있게 해야할 때가 있는데 이런 경우 check 함수를 통해 상태에 제한을 검
예시
fun speak(text: String) {
check(isInitialized)
// ...
}
- check 함수는 require과 비슷하지만 지정된 예측을 만족하지 못할 때 IllegalStateException 을 throw함
- 일반적으로 함수 전체에 대한 어떤 에측이 있을대는 require 블록 뒤에 배치
Assert 계열 함수 사용
fun pop(num: Int = 1): List<T> {
assert(ret.size == num)
return ret
}
- Assert 계열의 함수는 코드를 자체 점검
- 특정 상황이 아닌 모든 상황에 대한 테스트 할 수 있음
- 실행 시점에 정확하게 어떻게 되는지 확인할 수 있음
nullability와 스마트 캐스팅
- 코틀린에서 require와 check 블록으로 어떤 조건을 확인해서 true가 나왔다면 해당 조건 이후로도 true일 것이라고 가정해 이를 활용해 타입 비교를 했을시 스마트 캐스트가 작동
- 뿐만 아니라 nullability 목적으로 오른쪽에 throw 또는 return을 두고 Elvis 연산자를 활용하는 경우도 많음
스마트 캐스팅 예시
fun changeDress(person: Person) {
require(person.outfit is Dress)
val dress: Dress = person.outfit
//...
}
nullability 예시
fun sendEmail(person: Person, text: String) {
val email: String = person.email ?: return
}
정리
- 코틀린에서 코드에 제한을 걸때의 장점
- 제한을 훨씬 더 쉽게 확인할 수 있음
- 애플리케이션을 더 안정적으로 지킬 수 있음
- 코드를 잘못 쓰는 상황을 막을 수 있음
- 스마트 캐스팅 활용할 수 있음
- 코틀린에서 제공하는 제한 매커니즘
- require 블록 : 아규먼트와 관련된 에측을 정의할 때 사용하는 범용적인 방법
- check 블록 : 상태와 관련된 에측을 정의할 때 사용하는 범용적인 방법
- assert 블록 : 테스트 모드에서 테스트할 때 사용하는 범용적인 방법
- return과 throw 와 함께 Elvis 연산자 사용
'Reading Record > 이펙티브 코틀린' 카테고리의 다른 글
[이펙티브 코틀린] Item7. 결과 부족이 발생할 경우 null과 failure를 사용하라 (0) | 2022.04.19 |
---|---|
[이펙티브 코틀린] Item6. 사용자 정의 오류보다는 표준 오류를 사용하라 (0) | 2022.04.06 |
[이펙티브 코틀린] Item4. inferred 타입으로 리턴하지 말라 (0) | 2022.04.06 |
[이펙티브 코틀린] Item3. 최대한 플랫폼 타입을 사용하지 마라 (0) | 2022.04.06 |
[이펙티브 코틀린] Item 2. 변수의 스코프를 최소화하라 (0) | 2022.04.06 |