Kotlin

Kotlin - 제네릭

J_Bin 2022. 6. 20. 13:43

# 제네릭 : 제네릭(Generic)은 클래스 내부에서 사용할 자료형을 나중에 인스턴스를 생성할 때 확정한다.

제네릭을 사용하면 객체의 자료형을 컴파일할 때 체크하기 떄문에 객체 자료형의 안정성을 높이고 형 변환의 번거로움이 줄어든다. 의도하지 않은 자료형의 객체를 지정하는 것을 막고 객체를 사용할 때 원래의 자료형에서 다른 자료형으로 형 변환 시 발생할 수 있는 오류를 줄인다.

 

 

package chap02.section1

/* 간단한 제네릭의 예제 */

class Box<T>(t: T) {        // 형식 매개변수로 받은 인자를 name에 저장
    var name = t
}

fun main() {

    val box1 : Box<Int> = Box<Int>(1)                   // 인스턴스 생성 시 Int형으로 자료형이 결정됨
    val box2 : Box<String> = Box<String>("Hello")       // 인스턴스 생성 시 String형으로 자료형이 결정됨

    println(box1.name)
    println(box2.name)


}

/* Box<T>에서 T가 바로 형식 매개변수 이름이다. 보통 Type을 줄인 T를 사용하지만,
* 꼭 T를 사용하지는 않는다. 
* 그 밖에 제네릭에서 사용하는 형식 매개변수 이름들 : 
* E (요소 - Element)
* K (키  - Key)
* N (숫자 - Number)
* T (형식 - type)
* V (값 - Value)
* S,U,V etc. (2번째,3번째,4번째 형식)
* */

 

# 제네릭 클래스의 자료형 변환

package chap02.section1

/* 제네릭 클래스의 자료형 변환하기 */

open class Parent

class Child : Parent()

class Cup<T>





fun main() {

    val obj1 : Parent = Child()     // Parent 형식의 obj1은 Child의 자료형으로 변환될 수 있음
    val obj2 : Child = Parent()     // 자료형 불일치!

    val obj3 : Cup<Parent> = Cup<Child>()       // 자료형 불일치!
    val obj4 : Cup<Child> = Cup<Parent>()       // 자료형 불일치!

    val obj5 = Cup<Child>()                     // obj5는 Cup<Child>의 자료형이 됨
    val obj6 : Cup<Child> = obj5                // 자료형 일치!

}

 

# 형식 매개변수에 null이 가능한 제네릭 클래스

 

package chap02.section1

/* 제네릭 클래스의 자료형 변환하기 */

class GenericNull<T> {                      // 기본적으로 null이 허용되는 형식 매개변수
    fun EqualityFunc(arg1: T, arg2: T) {
        println(arg1?.equals(arg2))
    }
}





fun main() {

    val obj = GenericNull<String>()                     // non-null로 선언됨
    obj.EqualityFunc("Hello","World")         // null이 허용되지 않음

    val obj2 = GenericNull<Int?>()                      // null이 가능한 형식으로 선언됨
    obj2.EqualityFunc(null,20)                // null 사용 가능

}
package chap02.section1

/* 제네릭 클래스의 자료형 변환하기 */

class GenericNull<T:Any> {                      // 자료형 Any가 지정되어 null을 허용하지 않음
    fun EqualityFunc(arg1: T, arg2: T) {
        println(arg1?.equals(arg2))
    }
}





fun main() {

    val obj = GenericNull<String>()                     // non-null로 선언됨
    obj.EqualityFunc("Hello","World")         // null이 허용되지 않음

    val obj2 = GenericNull<Int?>()                      // 오류! null 허용이 안됨
    obj2.EqualityFunc(null,20)                

}

 

# 제네릭 함수

package chap02.section1


// 제네릭 함수(메서드)

/* 배열의 인덱스 찾아내기 */

// 제네릭 함수 선언
// a : Array<T> (자료형 아무거나 받을 수 있음)
// Target : T (자료형 아무거나 받을 수 있음)

fun <T> find(a : Array<T>, Target : T) : Int {
    for (i in a.indices){
        if (a[i] == Target) return i
    }
    return -1
}

fun main() {

    val arr1: Array<String> = arrayOf("Apple","Banana","Cherry","Durian")
    val arr2: Array<Int> = arrayOf(1,2,3,4)

    println("arr.indices ${arr1.indices}")              // indices는 배열의 유효 범위 반환
    println(find<String>(arr1,"Cherry"))          // 요소 Cherry의 인덱스 찾아내기
    println(find(arr2,2))                         // 요소 2의 인덱스 찾아내기
    println(find(arr1,"orange"))                  // 요소 orange의 인덱스 찾아내기 -> orange라는 요소가 없기 때문에 -1 반환




}

 

# 제네릭과 람다식

package chap02.section1


/* 제네릭과 람다식 */

fun <T> add(a: T, b: T, op : (T, T) -> T): T {
    return op(a,b)
}



fun main() {

    val result = add(2,3) { a, b -> a + b }
    println(result)

        /* 람다식 { a,b -> a + b}은 add() 함수가 실행될 때 넘겨지는 인자이므로
        *  연산식을 함수 선언부에 직접 구현하지 않고 전달하는 방법을 사용한다. 따라서 함수의 형식 매개변수의 자료형을
        *  특정하지 않아도 실행이 가능한 것이다.
        * */


}

# 자료형 제한하기

package chap02.section1


/* 클래스 및 함수에서 형식 매개변수의 자료형 제한하기 */

// 자료형을 숫자형으로 제한하기
// Number : Int, Double, Long..과 같은 숫자형
class Calc<T: Number> {                 // 클래스의 형식 매개변수 제한
    fun plus(arg1 : T, arg2 : T) : Double {
        return arg1.toDouble() + arg2.toDouble()
    }
}

// 함수에서 형식 매개변수의 자료형 제한하기(Number로 제한함)
fun <T: Number> addLimit(a: T, b: T, op : (T,T) -> T) : T {
    return op(a,b)
}


fun main() {

    val calc = Calc<Int>()
    println(calc.plus(10,20))

    val calc2 = Calc<Double>()
    val calc3 = Calc<Long>()
    //val calc4 = Calc<String>                                      // 제한된 자료형으로 인해 오류 발생

    println(calc2.plus(2.5,3.2))
    println(calc3.plus(21L,12L))


    //val result = addLimit("abc","def") { a, b -> a + b }     // 제한된 자료형으로 인해 오류 발생
    val result1 = addLimit(1,2) {a,b -> a + b}
    
    


}

 

# 다수 조건의 형식 매개변수 제한하기

package chap02.section1


interface InterfaceA
interface InterfaceB

class HandlerA : InterfaceA,InterfaceB
class HandlerB : InterfaceA

class ClassA<T> where T: InterfaceA, T:InterfaceB       // 2개의 인터페이스를 구현하는 클래스로 제한


fun main() {

    val ob1 = ClassA<HandlerA>()                        // 객체 생성 가능
    val ob2 = ClassA<HandlerB>()                        // 범위에 없으므로 에러 발생!

}

/**/

'Kotlin' 카테고리의 다른 글

Kotlin - 컬렉션  (0) 2022.06.21
Kotlin - 배열  (0) 2022.06.20
Kotlin - 여러 연산자들의 표현  (0) 2022.06.20
Kotlin - 데이터 클래스(Java의 POJO)와 기타클래스  (0) 2022.06.17
Kotlin - 추상클래스와 인터페이스 ★  (0) 2022.06.15