공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!! 포스팅 내용이 찾아주신 분들께 도움이 되길 바라며 더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^
|
이번 포스팅에서는 타입 체크와 캐스팅에 대해서 공부합니다.
is and !is 연산자
is 나 !is 연산자를 사용하면 런타임에 객체가 주어진 타입을 따르는지 확인할 수 있습니다.
val str = "str"
if(str is String) println("good")
if(str !is String ) println("not good")
else println("good")
Smart Casts
컴파일러가 불변 값에 대해서 타입 체크와 명시적 캐스트 추적하고, 필요한 경우에
자동으로 캐스팅을 하기 때문에 대부분의 경우 명시적으로 캐스팅 할 필요가 없습니다.
fun castMethod(a: Any) {
if (a is String) {
print(a.length) // a가 String 타입으로 자동 캐스팅
}
}
컴파일러는 안전한 캐스팅을 판단할 수 있을만큼 스마트합니다.
그래서 아래 코드들에서도 정상적으로 잘 동작합니다.
val x = ""
if (x !is String) return
print(x.length) // x가 String 타입으로 자동 캐스팅
// x is automatically cast to string on the right-hand side of `||`
if (x !is String || x.length == 0) return
// x is automatically cast to string on the right-hand side of `&&`
if (x is String && x.length > 0) {
print(x.length) // x is automatically cast to String
}
뿐만 아니라 when 표현식이나 while 문에서도 잘 동작합니다.
when (x) {
is Int -> print(x + 1)
is String -> print(x.length + 1)
is IntArray -> print(x.sum())
}
* 스마트 캐스트는 변수의 체크와 사용 사이에 변경되지 않는 것을
보장할 수 없다면 동작하지 않습니다.
조금 더 구체적으로 스마트 캐스트는 아래 규칙에 따라 적용할 수 있습니다.
- val local variables : local delegated properties을 제외한 모든 로컬 변수에는 항상 적용
- val properties : 프로퍼티가 private 이거나 internal 이거나 동일 한 모듈 내에서
검사가 수행되는 경우 적용. open properties 나 custom getters 를 가지는 프로퍼티에는
스마트 캐스트가 동작하지 않습니다.
- var local variables : 변수가 체크와 사용 사이에 수정되지 않으면
변수를 수정하는 람다에서 캡처되지 않고 로컬 위임 된 속성이 아닌 경우
- var properties 에는 적용 x
---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
"Unsafe" cast operator
일반적으로, 캐스트 연산자는 캐스트 할 수 없는 경우에 exception을 던집니다.
그러므로 unsafe 라고 부릅니다. unsafe cast는 코틀린에서 as 연산자로 사용합니다.
var x : String = y as String
* null은 String으로 캐스팅 될 수 없습니다. 즉 위 코드에서 y 가 null 이라면 exception이
발생합니다.
때문에 아래처럼 사용합니다.
var x : String? = y as String?
"Safe" (nullable) cast operator
exception의 발생을 피하려면 안전한 연산자인 as? 를 사용하면 됩니다.
as? 를 사용하면 캐스팅이 안될 경우 null을 반환합니다.
val y : Int? = 10
var x : String? = y as? String
print(x)
타입 지우기와 제네릭 타입 체크
코틀린은 컴파일 시 제네릭을 포함하는 연산의 type safety 를 보장하는 반면,
런타입 시에는 일반 타입의 인스턴스는 실제 타입 argument에 대한 정보를 가지고 있지
않습니다. 예를 들어 List<Foo> 는 List<*> 처럼 사용됩니다.
일반적으로 런타임 시에 어떠한 타입이 특정 타입에 속해있는지
확인할 수 있는 방법이 없습니다.
주어진 점을 감안했을 때 컴파일러는 ints is List<Int> 이거나
list is T 같이 타입 삭제로 인해 런타임에 수행할 수 없는 is 체크를 금지합니다.
하지만 아래와 같이 * 를 사용한 타입 확인은 가능합니다.
if ( things is List<*>) {
thing.forEach{ print(it) }
}
유사하게 인스턴스의 type arguments 가 정적으로 체크되었다면
is 체크를 하거나 비 제네릭 타입이 포함된 캐스팅을 할 수 있습니다.
그리고 이 경우에는 <> 는 생략됩니다.
fun handleStrings(list: List<String>) {
if (list is ArrayList) {
// `list` is smart-cast to `ArrayList<String>`
}
}
Unchecked Casts
위에서 말했듯이 type erasure는 런타임 시 불가능한 제네릭 형식 인스턴스의
실제 형식 인수를 검사 할 수 없으며 코드의 제네릭 형식은
형식 안전을 보장하기 위해 컴파일러가 충분히 밀접하게 연결되어 있지 않을 수 있습니다.
그렇다고 하더라도 때때로 type safety를 의미하는 high-level program logic이 있습니다.
예)
fun readDictionary(file: File): Map<String, *> = file.inputStream().use { TODO("Read a mapping of strings to arbitrary elements.") } // We saved a map with `Int`s into that file val intsFile = File("ints.dictionary") // Warning: Unchecked cast: `Map<String, *>` to `Map<String, Int>` val intsDictionary: Map<String, Int> = readDictionary(intsFile) as Map<String, Int>
이상입니다.
더 자세한 내용은 아래 링크에서 확인하시면 되겠습니다~
https://kotlinlang.org/docs/reference/typecasts.html
감사합니다.
'코틀린(Kotlin)' 카테고리의 다른 글
코틀린(kotlin) : 단항 연산자, 이항연산자, 여러 연산자 (0) | 2018.02.08 |
---|---|
코틀린(kotlin) : this 와 Equality (0) | 2018.02.08 |
코틀린(kotlin) : Ranges 범위 표현 (0) | 2018.02.07 |
코틀린(kotlin) : Collections : List, Set, Map (0) | 2018.02.07 |
코틀린(kotlin) : Destructuring Declaration (0) | 2018.02.06 |