코틀린(Kotlin)

코틀린(kotlin) : 타입체크 is 와 캐스팅 as Type Checks and Casts

알통몬_ 2018. 2. 7. 16:43
반응형


공감 및 댓글은 포스팅 하는데

 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 

도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 

만들어 나가겠습니다^^

 


이번 포스팅에서는 타입 체크와 캐스팅에 대해서 공부합니다.


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

감사합니다.

반응형