코드테스트 사이트: 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 |