Archives
Recent Posts
«   2025/02   »
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
Today
Total
관리 메뉴

안드로이드 개발자의 창고

[16일차 Kotlin] Generic 본문

Computer/Kotlin

[16일차 Kotlin] Generic

Wise-99 2023. 5. 18. 23:29

 

 

출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT

 

 

 

📖 Generic

  • 클래스를 설계할 때 변수의 타입을 유동적으로 하고 싶을 때가 있을 수도 있다.
  • 이 때 Generic 개념을 활용하면 클래스 작성시가 아닌 객체 생성 시에 변수의 타입을 설정할 수 있다.

가변성

  • 불변성 : Generic이 설정된 객체의 주소 값을 같은 타입의 제네릭이 설정된 변수에만 담을 수 있다.
  • 공변성 : Generic이 설정된 객체의 주소 값을 부모 클래스 타입의 제네릭이 설정된 변수에도 담을 수 있다(out)
  • 반 공변성 : Generic이 설정된 객체의 주소 값을 자식 클래스 타입의 제네릭이 설정된 변수에도 담을 수 있다(in)

 

 

 

📖 예제 코드

Generic이 설정된 Class

class TestClass1<T>{
    fun testMethod1(a1:T){
        println("a1 : $a1")
    }
}

class TestClass2<T>(var a1 : T){
    fun testMethod2(a2 : T){
        println("a1 : $a1")
        println("a2 : $a2")
    }
}

class TestClass3<A, B, C, D>(var a1:A, var a2:B){
    fun testMethod3(a3:C, a4:D){
        println("a1 : $a1")
        println("a2 : $a2")
        println("a3 : $a3")
        println("a4 : $a4")
    }
}

✔️ 코드 해석

  • < > : 안에 알파벳 문자를 넣어준다. 보통 대문자 한글자를 작성한다.
  • 이 알파벳은 아직 결정되지 않은 타입을 의미한다.
  • 어떠한 타입인지 결정되지 않았지만 이 타입의 변수들을 정의하여 클래스를 작성하고, 객체를 생성할 때 타입을 결정할 수 있다.

 

 

 

Generic 클래스를 이용한 코드

fun main(){
    val t1 = TestClass1<Int>()
    t1.testMethod1(100)

    val t2 = TestClass1<String>()
    t2.testMethod1("안녕하세요")

    val t3 = TestClass2<Int>(100)
    t3.testMethod2(200)

    val t4 = TestClass2<String>("문자열1")
    t4.testMethod2("문자열2")

    val t5 = TestClass3<Int, Double, Boolean, String>(100,11.11)
    t5.testMethod3(true, "문자열1")

    val t6 = TestClass3<Double, String, Boolean, Int>(22.22, "문자열2")
    t6.testMethod3(false, 200)
}

✔️ 코드 해석

  • Generic이 정의된 클래스를 가지고 객체를 생성한다.
  • Generic 타입의 타입을 결정한다.

 


 

Generic 불변성, 공변성, 반 공변성 예제 클래스

open class SuperClass1
open class SubClass1 : SuperClass1()
class SubClass2 : SubClass1()

// 불변성
class TestClass5<A>()

// 공변성
class TestClass6<out A>()

// 반 공변성
class TestClass7<in A>()

 

 

 

main 예제 코드

fun main(){
    val t7:TestClass5<SubClass1> = TestClass5<SubClass1>()
    // val t8:TestClass5<SuperClass1> = TestClass5<SubClass1>()
    // val t9:TestClass5<SubClass2> = TestClass5<SubClass1>()

    val t10:TestClass6<SubClass1> = TestClass6<SubClass1>()
    val t11:TestClass6<SuperClass1> = TestClass6<SubClass1>()
    // val t12:TestClass6<SubClass2> = TestClass6<SubClass1>()

    val t13:TestClass7<SubClass1> = TestClass7<SubClass1>()
    // val t14:TestClass7<SuperClass1> = TestClass7<SubClass1>()
    val t15:TestClass7<SubClass2> = TestClass7<SubClass1>()
}

✔️ 코드 분석

  • val t7:TestClass5<SubClass1> = TestClass5<SubClass1>()
    • 불변성 : 객체를 생성할 때 설정한 Generic과 같은 변수에 담을 수 있다.
    • 클래스 간의 관계에 상관 없이 Generic에 설정한 클래스 타입이 다르면 오류가 발생한다.
    • 따라서 해당 코드 밑에 있는 t8과 t9는 오류가 발생한다.
  • val t11:TestClass6<SuperClass1> = TestClass6<SubClass1>()
    • TestClass6은 out 키워드를 사용했다.
    • 공변성 : 변수에 설정한 Generic이 객체를 생성했을 때 사용한 Generic의 부모 클래스인 경우에도 담을 수 있다.
    • 따라서 t11은 가능하지만 t12는 오류가 발생한다.
  • val t15:TestClass7<SubClass2> = TestClass7<SubClass1>()
    • TestClass7은 in 키워드를 사용했다.
    • 반 공변성 : 변수에 설정한 Generic이 객체를 생성했을 때 사용한 Generic의 자식 클래스인 경우에도 담을 수 있다.