Archives
Recent Posts
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Today
Total
관리 메뉴

안드로이드 개발자의 창고

[17일차 Kotlin] Casting(캐스팅) 본문

Computer/Kotlin

[17일차 Kotlin] Casting(캐스팅)

Wise-99 2023. 5. 20. 02:36

📖 캐스팅(형변환)

  • 변수에 담긴 값이나 객체를 다른 형태로 변환하는 것을 의미한다.
  • Kotlin은 모든 값을 객체로 관리하기 때문에 Kotlin에서의 형변환은 다른 클래스 타입의 객체로 변환하는 것을 의미한다.
  • 자식 클래스 타입으로의 변환
  • 부모 클래스 타입으로의 변환
  • 다른 자료형 타입으로의 변환
  • null 허용과 null 불허용 간의 변환

스마트 캐스팅(Smart Casting)

  • 특정 조건을 만족하면 자동으로 형변환이 발생하는 개념이다.
  • 스마트 캐스팅 기능 덕분에 형변환에 대해 개발자가 크게 신경을 쓰지 않아도 된다.
  • 형 변환은 객체의 클래스 타입이 아닌 객체의 주소 값을 가지고 있는 참조 변수의 타입이 변경되는 것이다.

객체 타입 변환

  • 객체의 타입 변환은 상속관계나 구현한 인터페이스 타입에 해당한다.
  • 부모 클래스 타입으로의 형 변환
  • 자식 클래스 타입으로의 형 변환
  • 구현한 인터페이스 타입으로의 형 변환
  • 인터페이스를 구현한 클래스 타입으로의 형 변환

as 연산자

  • 객체를 지정된 클래스 타입으로 변환하는 연산자이다.
  • 참조변수 as 클래스타입
  • 만약 객체가 지정된 클래스타입과 관계가 없을 경우 오류가 발생한다.
  • 형 변환이 발생한 참조 변수는 변환된 타입을 유지한다.

is 연산자

  • 형 변환이 가능하면 변환을 하고 true를 반환한다.
  • if 문으로 구성하여 사용하며 if 문 내에서만 변환된 타입을 사용하고 if 문을 나가게 되면 변환되기 전의 타입으로 다시 변경된다.

Any 타입

  • Kotlin은 모든 클래스가 직접 혹은 간접적으로 Any 클래스를 상속받는다.
  • 따라서 모든 객체의 주소 값은 Any 타입 참조 변수에 담을 수 있다.
  • Any 타입과 is 연산자를 활용하여 다양한 타입의 객체에 대응할 수 있는 코드를 만들 수 있다.

기본 타입의 형변환

  • Kotlin 에서는 기본 타입을 관리하는 객체의 타입을 변경하는 메서드를 제공한다.
  • 참조 변수의 타입이 변경되는 것이 아닌 새로운 객체가 생성되어 반환된다.
  • toByte(), toShort(), toInt(), toLong(), toFloat(), toDouble(), toChar()

 

 

 

📖 예제 코드

open class SuperClass1
interface Inter1

class SubClass1 : SuperClass1(){
    fun subMethod1(){
        println("SubClass1의 subMethod1입니다.")
    }
}

class SubClass2 : Inter1{
    fun subMethod2(){
        println("SubClass2의 subMethod2입니다.")
    }
}

class SubClass3

 

 

 

fun main() {
    val obj1:SubClass1 = SubClass1()
    val obj2:SubClass2 = SubClass2()

    val obj3:SuperClass1 = obj1
    val obj4:Inter1 = obj2

    println("obj3 : $obj3")
    println("obj4 : $obj4")
    
    obj3.subMethod1()    <<< 오류 발생
    obj4.subMethod2()    <<< 오류 발생
    
    obj3 as SubClass1
    obj3.subMethod1() // SubClass1의 subMethod1입니다.

    obj4 as SubClass2
    obj4.subMethod2() // SubClass2의 subMethod2입니다.
    
    val temp2:SubClass3 = SubClass3()

    temp2 as SuperClass1    <<< 오류 발생
    temp2 as Inter1    <<< 오류 발생
    
    val obj5:SubClass1 = SubClass1()
    
    val temp3:SuperClass1 = obj5
    val temp4 = obj5 as SuperClass1
    
    println("temp3 : $temp3") // SubClass1@ ~~~
    println("temp4 : $temp4") // SubClass1@ ~~~

    val chk1 = temp4 is SubClass1
    val chk2 = temp4 is SuperClass1
    val chk3 = temp4 is Inter1

    println("chk1 : $chk1") // true
    println("chk2 : $chk2") // true
    println("chk3 : $chk3") // false
}

✔️ 코드 해석

  • obj3.subMethod1() / obj4.subMethod2()
    • 객체에 접근할 때 부모 클래스 혹은 구현한 인터페이스 타입으로 접근하면 자식 클래스에 있는 메서드 중 overriding한 것이 아닌 것은 호출할 수 없다.
  • obj3 as SubClass1 / obj4 as SubClass2
    • 부모클래스 타입형 변수에 담긴 객체를 자식 클래스 형으로 변환한다.
    • as : 변수의 타입을 다른 클래스 타입으로 변환하는 연산자
    • 상속 관계에 있거나 구현한 인터페이스 관계가 있을 경우에만 성공한다.
    • 스마트 캐스팅에 해당되지 않는다.
  • temp2 as SuperClass1 / temp2 as Inter1
    • 상속관계나 인터페이스 구현 관계가 아닌 객체의 형변환
    • 전혀 관계가 없다고 하더라도 문법적으로 오류가 발생하지는 않는다.
    • 실행 중에 오류가 발생한다.
  • val temp3:SuperClass1 = obj5
    • 객체를 담을 변수에 타입을 설정하면 형변환이 자동으로 이루어진다.
  • val temp4 = obj5 as SuperClass1
    • 객체를 담을 변수에 타입을 설정하지 않으면 변수의 타입은 객체의 클래스 타입으로 결정된다.
    • 이럴 때는 as 이용해 형변환해주면 형변환된 클래스타입 변수로 정의된다.
  • val chk1 = temp4 is SubClass1 / val chk2 = temp4 is SuperClass1 / val chk3 = temp4 is Inter1
    • 참조변수를 통해 접근할 수 있는 객체에 해당 클래스 영역이 있는지 검사한다.

fun main(){
    // 스마트 캐스팅
    val super3:SuperClass1 = SubClass1()
    
    if(super3 is SubClass1){
        super3.subMethod1()
    }
    
    super3.subMethod1()    <<< 오류 발생
    
    val obj10 = SubClass1()
    val obj11 = SubClass2()
    val obj12 = 100
    val obj13 = "문자열"

    anyMethod(obj10)
    anyMethod(obj11)
    anyMethod(obj12)
    anyMethod(obj13)
}

fun anyMethod(obj:Any){
    if(obj is SubClass1){
        obj.subMethod1()
    }
    if(obj is SubClass2){
        obj.subMethod2()
    }
    if(obj is Int){
        println("정수입니다")
    }
    if(obj is String){
        println("문자열 입니다")
    }
}

✔️ 코드 해석

  • val super3:SuperClass1 = SubClass1()
    • 참조변수(super3)를 통해 접근할 수 있는 객체에 원하는 클래스의 영역(SubClass1)이 있는 경우 자동으로 형변환을 해준다.
  • if문 밖에 있는 super3.subMethod1()
    • if 문 밖으로 나가면 형변환 되지 않은 상태가 된다.
    • SuperClass1에서는 SubClass1의 메서드에 접근할 수 없기 때문에 오류가 발생한다.
  • fun anyMethod(obj:Any){ ... }
    • 스마트 캐스팅과 Any를 이용하면 하나의 메서드로 여러 클래스타입의 객체에 대한 작업을 수행할 수 있다.
    • obj10은 subMethod1()을 호출한다.
    • obj11은 subMethod2()를 호출한다.
    • obj12는 "정수입니다"를 출력한다.
    • obj13은 "문자열 입니다"를 출력한다.

fun main(){
    val number1:Int = 100
    val number2:Long = number1.toLong()
    println("number2 : $number2")

    val str1:String = "100"
    val number3:Int = str1.toInt()
    println("number3 : $number3")

    val str2:String = "안녕하세요"
    val number4:Int = str2.toInt()    <<< 오류 발생
    println("number4 : $number4")
}

✔️ 코드 해석

  • val number2:Long = number1.toLong()
    • 더 큰 자료형에 값을 저장한다.
    • Int는 4bute, Long은 8byte
  • val number4:Int = str2.toInt()
    • 문자열이 숫자로만 구성된 것이 아니기 때문에 숫자로 변환시 오류가 발생한다.