코틀린

[kotlinlang.org] 한글화. 2 유형 검사

iseohyun 2022. 5. 6.

목차

    [원본] https://kotlinlang.org/docs/typecasts.html

     

    is, !is

    변수 is 타입 : 리턴 = true/false
    fun main() {
        var obj = "가나다라마"
        if (obj is String) {
            println(obj.length)
        }
    
        if (obj !is String) { // 다음과 같습니다 : !(obj is String)
            println("Not a String")
        } else {
            println(obj.length)
        }
    }
    5
    5

     

    스마트케스트

    아래 경우에는 정해지지 않은 타입이라도 유추할 수 있습니다.

    Any : 정해지지 않은 타입
    is연산자를 통해서 타입을 보장할 수 있는 경우, 해당 타입으로 케스트(사용)할 수 있습니다.

    경우1 : if 이하에서..

    fun main(){
        type("문자열")
        type(1)
    }
    
    fun type(x : Any) {
        if (x is String) {	// is String을 통과했으므로
            println(x.length) // x가 문자열이라고 단정할 수 있습니다.
        } else {
            print("문자열이 아님")
        }
    }
    3
    문자열이 아님

    경우2 : return이하에서.. (결과는 1과 동일)

    fun main(){
        type("문자열")
        type(1)
    }
    
    fun type(x : Any) {
        if (x !is String) {	
            println("문자열이 아님")
            return
        } // !is String를 통과하지 못한다면,
        
        println(x.length) // x가 문자열이라고 단정할 수 있습니다.
    }

    경우3 : if문이 And, Or로 연결되었다면..

    fun main(){
        type("문자열")
        type(1)
        type("")
    }
    
    fun type(x : Any) {
        // if의 조건에서, `||`보다 왼편에 있는 조건을 만족해야, 오른쪽 조건을 살펴본다.
        // 따라서 x.length는 에러가 나지 않는다.
        if (x !is String || x.length == 0){
            println("길이가 없습니다.")
            return  
        }
        
        // if의 조건에서, '&&'를 만족했다면 x.lenght > 0에서 x를 String으로 유추 할 수 있다.
        if (x is String && x.length > 0) {
            println(x.length) // x를 String으로 인식한다.
        }
    }
    3
    길이가 없습니다.
    길이가 없습니다.

     

    안전하지 않은 캐스팅 as!

    excpetion(예외, 오류)이 발생할 수 있는 캐스팅을 '안전하지 않은 캐스팅'이라고 합니다. as를 사용합니다.

    fun main(){
        val y: String? = null
        
        // 예외처리가 발생합니다.
        //val x: String = y as String
        
        // 해결1 : 입/출력 모두 nullalbe로 만듭니다.
        val x: String? = y as String?
        
        // 해결2 : 변경과정에서 예외처리가 발생하는 것을 막으려면 as?를 이용해서 보호합니다.
        val z: String? = y as? String
        
        println(x)
    }
    null

     

    유형 삭제 및 일반 유형 검사

    컴파일하는 동안 제네릭정보가 삭제됩니다. 예를들어 List<Foo> 는 List<*>가 됩니다. 런타임에 제네릭이 특정형식을 가지고 있는지 알 수 없습니다. 따라서 해당형식이 List인지 확인하려면 List<*>를 사용합니다.

    ※ List역시 Class로 관리된다는 점을 고려한다면, is연산자로 자동캐스팅된다는 점은 놀라운 점이 아닙니다.

    컴파일시에 제네릭(<>)은 삭제된다.

    이 사실만 명심하면 될 것 같습니다.

    fun main(){
        var something = listOf(1, 2, 3, 4, 5)
        if (something is List<*>) {
            something.forEach { println(it) } // `Any?`로 유추됩니다.
        }
    }
    1
    2
    3
    4
    5

     

    is 연산자를 통해서 type을 캐스팅 할 수도 있습니다. (위 예제어서 <*>만 생략됨)

    fun main(){
        var something = arrayListOf("하나", "둘", "셋")
        var anything = listOf("넷", "다섯", "여섯")
        handleStrings(something)
        handleStrings(anything)
    }
    
    fun handleStrings(list: List<String>) {
        if (list is ArrayList) {
            // `list`가 `ArrayList<String>`로 자동캐스팅 됩니다.
            list.forEach { print("$it ") }
            println()
        }else{
            print("ArrayList가 아님니다.")
        }
    }
    하나 둘 셋 
    ArrayList가 아님니다.

    ※ 아래 예제는 원문에서 발췌한 코드입니다.
    예문의 내용은 제네릭이 런타임에서 삭제되어있다는 내용이며, 함수파트를 학습한 뒤에 이해할 수 있습니다.

    inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
        if (first !is A || second !is B) return null
        return first as A to second as B
    }
    
    val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)
    
    
    val stringToSomething = somePair.asPairOf<String, Any>()
    val stringToInt = somePair.asPairOf<String, Int>()
    val stringToList = somePair.asPairOf<String, List<*>>()
    val stringToStringList = somePair.asPairOf<String, List<String>>() // Compiles but breaks type safety!
    // Expand the sample for more details
    
    
    fun main() {
        println("stringToSomething = " + stringToSomething)
        println("stringToInt = " + stringToInt)
        println("stringToList = " + stringToList)
        println("stringToStringList = " + stringToStringList)
        //println(stringToStringList?.second?.forEach() {it.length}) // This will throw ClassCastException as list items are not String
    }

     

    확인되지 않는 캐스트

    본문의 예문은 "readDictionary함수가 반환하는 타입"과 "마지막 줄에서 반환받는 타입"이 다른 것을 알 수 있습니다. 이 경우 일단 컴파일이 되기는 하지만, 컴파일러에서 경고를 낼 수 있습니다. 경고를 없애기 위해서 다음을 사용합니다.

    @Suppress("UNCHECKED_CAST")
    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 this 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>

     

    댓글