오늘은 Kotlin의 Smart Cast에 대해 정리하겠습니다.
1. Smart Cast(스마트 캐스트)
Smart Cast(스마트캐스트)는 이름 자체에서 알 수 있듯이,
컴파일러가 타입을 자동으로 추론해서,
명시적으로 타입을 변환하지 않아도,
사용하도록 해주는 기능입니다.
타입을 검사하고, 변환하는 것까지,
자동으로 Smart하게 해주는 것 이지요.
이를 이용하면,
코드가 짧아져 가독성이 좋아지고,
유지보수성도 좋아집니다.
2. is를 사용한 Smart Cast
2-1. is smart cast
가장 기본적인 스마트 캐스트의 사용법은,
is 키워드를 사용해,
변수의 타입을 검사한 후,
해당 타입으로 자동 캐스트하는 것 입니다.
아래는 'is'키워드를 사용해,
any의 타입검사를 하고,
String에 해당하면 String으로 변환해 줍니다.
자동으로 변환되어서, 길이를 알아낼 수 있습니다.
fun getStringLength(any: Any): Int {
if (any is String) {
// any가 자동으로 String 타입으로 스마트 캐스트
return any.length
}
return 0
}
조금 더 복잡한 사례도 볼까요?
아래와 같은 sealed Class로 State를 정의한 코드가 있습니다.
sealed class LoginState {
data object Loading : LoginState()
data object NetworkError : LoginState()
data class Success(val username: String, val token: String) : LoginState()
data class Error(val message: String) : LoginState()
}
object는 싱글톤으로 바로 접근이 가능하지만,
data class로 선언된 Success와 Error는,
아래와 같이 is로 타입을 체크하고 자동으로 변환 받아서,
블록 내부에서 state.username과 같이 접근해서 사용할 수 있습니다.
fun handleLoginState(state: LoginState) {
when (state) {
LoginState.Loading -> showLoadingSpinner()
LoginState.NetworkError -> showNetworkError()
is LoginState.Success -> {
// is로 smart cast
navigateToMain(state.username)
saveToken(state.token)
}
is LoginState.Error -> showErrorDialog(state.message)
}
}
2-2. as와 비교하기
비슷한 키워드로 as가 있는데요.
이는 Smart Cast가 아닙니다.
유저가 명시적으로 특정 타입으로 캐스팅하는 키워드인데요.
아래와 같이 사용합니다.
아래 첫번째는 any를 String으로 강제적으로 변환한 코드인데,
만약 String이 아니면, ClassCastException이 발생하게 됩니다.
fun explicitCastExample(any: Any) {
val str = any as String // 강제 타입 변환
println(str.length)
// 만약 any가 String이 아니면 ClassCastException 발생
}
fun safeCastExample(any: Any) {
val str = any as? String // 실패하면 null 반환
println(str?.length) // null safety 처리
}
is와 as 어떻게 다른지 알아두면 구분해 사용하는데 도움이 되겠지요.
3. non-null캐스팅
Null 체크할 때도 활용할 수 있는데요.
아래와 같이 if문으로 null체크를 하면,
'str.length'를 '?'없이도 사용할 수 있습니다.
fun processNullableString(str: String?) {
if (str != null) {
// str이 자동으로 non-null String 타입으로 스마트 캐스트
println("길이: ${str.length}")
}
}
그런데, 해보신 분들 중,
null체크하고 블록 안에 들어갔는데,
smart cast가 동작하지 않는 경험을 했을 수도 있는데요.
이런 경우는 대부분,
해당 값이 var로 선언된 변수이기 때문입니다.
이는 var로 선언된 변수가,
null 체크 이후에도 값이 변경될 가능성이 있고,
다른 스레드에서 값이 변경될 수도 있기 때문입니다.
이럴 때는 다음과 같이 로컬변수로 복사해서 사용하는 방법을 사용하면됩니다.
val verifiedName = mutableName
if (verifiedName != null) {
println("길이: ${verifiedName.length}")
}
4. requireNotNull 과 checkNotNull
null을 체크하는 방법으로,
requireNotNull이나 checkNotNull을 이용하는 방법도 있습니다.
이 함수를 사용하는 것은,
Smart Cast처럼 기존 변수의 타입을 변경하는 것이 아닙니다.
그저 새로운 non-null 값을 복사하는 방법인데요.
null체크 후,
Smart Cast하는 것과 같이 알아두면 좋으니,
이 글에서 같이 정리하겠습니다.
4-1. requireNotNull 과 checkNotNull
두 함수는 비슷해 보이지만,
null일 경우 throw하는 Exception의 차이가 있습니다.
- requireNotNull: IllegalArgumentException 를 throw
- 함수에 전달된 인자가 잘못된 경우 주로 사용(파라미터 검증)
- checkNotNull: llegalStateException를 throw
- 주로 객체의 상태나 내부 로직 검증에 사용
checkNotNull을 사용한 예를 보겠습니다.
val verified = checkNotNull(name) { "초기화되지 않은 상태입니다" }
위의 코드를 실행했을 때,
name이 null일 경우,
아래와 같은 Exception이 발생합니다.
IllegalStateException: 초기화되지 않은 상태입니다
4-2. Elvis연산자와 함께 사용하기
requireNotNull과 checkNotNull은 정해진 기본 Exception만 던질 수 있는데요.
원하는 CustomException이 있다면,
Elvis 연산자를 이용해,
다음과 같이 사용할 수 있습니다.
val verifiedUsername = newUsername ?: throw UserNotFoundException("사용자 이름이 없습니다")
'Android 개발 > Kotlin' 카테고리의 다른 글
ifEmpty 와 orEmpty 에 대해서 알아보자 # Kotlin (0) | 2023.05.30 |
---|---|
CoerceIn, coerceAtMost, coerceAtLeast 범위지정 함수에 대한 정리 # Kotlin (0) | 2023.05.26 |
MapNotNull 과 MapNotNullTo 에 대한 정리 # null 제거 (0) | 2023.05.24 |
List 아이템 부분추출 함수 정리 # take, takeWhile, takeLast, drop, slice, first and last (0) | 2023.05.15 |
compareBy 와 min 그리고 sortedWith 사용방법에 대한 정리 (0) | 2023.05.10 |
any , contains, none , all, containsAll 에 대한 정리 # Kotlin list 존재여부 확인 (1) | 2023.05.06 |
Destructuring declaration 에 대해 알아보자 # 구조분해 선언 Kotlin (0) | 2023.05.01 |
Kotlin Escape 에 대한 정리 # RawString Escaped String Literal (0) | 2023.04.30 |
Kotlin GroupBy 구현과 정리 # List 그룹핑 (0) | 2023.04.28 |
getOrNull 과 getOrElse 에 대한 정리 # List Kotlin (0) | 2023.04.28 |
댓글