본문 바로가기
코틀린 문법

11. 제너릭

by 농농씨 2023. 5. 11.

코드테스트 사이트: play.kotlinlang.org

참조: 유튜브 디모의 코틀린

*맥북 탭끼리 이동 단축키:command+shift+{ , command+shift+}

 

//Generic:클래스나 함수에서 사용하는 자료형을 외부에서 지정할 수 있는 기능
//제너릭이란 어떤 기능?
//예를 들어, 클래스 A와 이를 상속받은 클래스 B가 있을 때,
//이 두클래스의 인스턴스를, 공용으로 사용하는 하나의함수에 패러미터로 받으려면?
// ex.fun castingExam(var a:A) ->B를 넣어도 자동A로 캐스팅됨
// ->두 클래스 모두 함수의 ₩ㅌ패러미터로 사용할 수 있음!(?)
//but, 캐스팅 연산을 거치는 것은 프로그램의 속도를 저하시킬 수 있음!
//sol)제너릭!
//제너릭이란? 함수나 클래스를 선언할 때 고정적인 자료형 대신 
//실제 자료형으로 대체되는 '타입 패러미터'를 받아 사용하는 방법.
//ex.fun <Int> genericFunc (param:Int):Int
//   class FenericClass <Int>(var pref:Int)
//타입 패러미터에 특정 자료형이 할당되면 제너릭을 사용하는 모든 코드는
//할당된 자료형으로 대체되어 컴파일됨
//따라서, 캐스팅 연산 없이도 자료형을 그대로 사용할 수 있게 됨!
//타입패러미터의 이름은 클래스 이름과 규칙은 같지만,
//일반적으로 'Type'의 이니셜인 'T'를 사용하는 것이 관례!
//그리고 여러개의 제너릭을 사용할 경우 T다음 T,U,V알파벳 순서 따름
//또한 제너릭을 특정한 수퍼클래스를 상속받은 클래스 타입으로만 제한하려면 
//<T:SuperClass> 처럼 콜론 쓰고 수퍼클래스명을 명시함.
//이렇게 선언된 제너릭은 어떻게 사용하는 걸까요? 
//ex)fun <T> genericFunc (var param:T){}
// 이렇게 함수에 제너릭을 선언한 경우 
//   genericFunc(1)//일반적인 함수처럼 사용하면 타입패러미터를 추론해주며
//   T를 자동으로 Int로!
//   class GenericClass<T>
//   클래스의 경우
//   GenericClass<Int>()//인스턴스를 만들 때 타입패러미터(Int)를
//   수동으로 지정하거나, 
//   class GenericClass<T>(var pref:T)
//   생성자에 제너릭이 사용된 경우
//   GenericClass(1)//지정하지 않아도 자동으로 추론됨!
//   T를 자동으로 Int로!
//신나는 실습시간~

 


fun main(){
    
    UsingGeneric(A()).doShouting()
    //main에서는 usinggeneric의 인스턴스를 만들되
    //패러미터로는 클래스A의 인스턴스를 받음. 그러고doshouting실행
    //UsingGeneric<A>(A()).doShouting()처럼
    //타입패러미터를 <A>붙여서 '수동'으로 전달할 수도 있지만 
    //생성자의 패러미터를 통해 클래스 A라는 것을 알 수 있기 때문에
    // (A의 인스턴스를 받았으니 T는 A겠군!)여기서는 생략해도 괜찮다~
    UsingGeneric(B()).doShouting()
    UsingGeneric(C()).doShouting()
    //클래스B,C도 같은방법으로 UsingGeneric에 넘겨서사용
    //
    
    
}
open class A{ //먼저 수퍼클래스로 사용될 open된 클래스 만듦
    open fun shout(){ //open함수 만듦
        println("A가 소리칩니다")
    }


class B : A() {//A를 상속받은 클래스 만듦
    override fun shout(){//A의 오픈함수 오버라이딩
        println("B가 소리칩니다")
    }
}

class C : A() { //A를 상속받은 또다른 클래스 생성
    override fun shout(){ //또 오버라이딩
        println("C가 소리칩니다")
    }
}

class UsingGeneric<T:A>(val t:T){
    //클래스를 만들되 수퍼클래스를 A로 제한한! 제너릭 T를 선언하고(T:A)
    //생성자에서는 제너릭T에 맞는 인스턴스를 속성 t로 받음(val t:T)
    fun doShouting(){//함수에 항상 ()괄호 넣는거 잊지말기!!!!
        t.shout() //받은 속성의 함수를 실행
    }
}

 

//사실 제너릭을 사용하지 않고 UsingGeneric의 생성자에서
//class UsingGeneric(val t:A)
// 수퍼클래스인 A로 캐스팅하여 shout을 호출하여도 동작은 같겠지만
//class UsingGeneric<T:A>(val t:T)
// 이렇게 제너릭을 사용하는 경우 사용할때 Generic이 자료형을 대체하게 되어
// 캐스팅을 방지할수있으므로 성능을 더 높일 수 있다~(흐음..)
//generic을 함수에 사용하는 법도 배워봅시다~
//

fun main(){
    
    UsingGeneric(A()).doShouting()    
    UsingGeneric(B()).doShouting()
    UsingGeneric(C()).doShouting()
    
    doShouting(B())//doShouting함수에 B의 인스턴스 넘김
    //함수 역시 제너릭의 타입을 '자동으로 추론'하므로 별도로 타입 패러미터에
    //자료형을 전달할 필요는 없다(흐음...)
    //출력해보면, 역시 '캐스팅 없이 B의 객체 그대로' 함수에서 사용하는것!
}

fun <T:A> doShouting(t:T){
    //fun 뒤에 수퍼클래스를 A로 제한한 제너릭T를 선언하고 함수 선언
    //패러미터로는 제너릭T에 맞는 인스턴스 t를 받음(t:T)
    t.shout()
}

open class A{ 
    open fun shout(){ 
        println("A가 소리칩니다")
    }


class B : A() {
    override fun shout(){
        println("B가 소리칩니다")
    }
}

class C : A() { 
    override fun shout(){ 
        println("C가 소리칩니다")
    }
}

class UsingGeneric<T:A>(val t:T){
    fun doShouting(){
        t.shout() 
    }
}

//제너릭은 많은 기본 클래스에서도 사용되고 있으니 알아둬야한다~

 

'코틀린 문법' 카테고리의 다른 글

13. 문자열을 다루는 법  (0) 2023.05.11
12. 리스트  (0) 2023.05.11
10. 클래스의 다형성  (0) 2023.05.04
9. 상속, 오버라이딩과 추상화  (0) 2023.04.30
8. 클래스, 생성자, 보조생성자  (0) 2023.04.30