본문 바로가기

공부 자료/코틀린[Kotlin]

[Atomic 41] 제네릭스 소개

여러 타입에 대해 작동할 수 있는 컴포넌트인
파라미터화한 타입을 만드는 제네릭스

 

 

오늘은 여러 가지 클래스에 적합한/관계있는 제네릭스에 대해 학습하는 아톰이다.

 


 

[ 제네릭스 ]

제네릭스

: 클래스나 함수를 작성할 때 타입 제약을 느슨하게 해 표현력을 최대로 제공

: 제네릭 타입 정의를 위해 클래스 이름 뒤에, 내부에 하나 이상의 제네릭 플레이스 홀더가 들어있는 부등호(<>)를 추가

 

import atomictest.eq

class GenericHolder<T> (private val value: T){ // T 타입의 객체를 저장 및 반환
	fun getValue(): T = value
}

fun main() {
    val hi = GenericHolder(Automobile("Ford"))
    val a: Automobile = hi.getValue() // 결과가 자동으로 올바른 타입으로 저장
    a eq "Automobile(brand=Ford)"

    val h2 = GenericHolder(1)
    val i : Int = h2.getValue() // 결과가 자동으로 올바른 타입으로 저장
    i eq 1

    val h3 = GenericHolder("Chartreuse")
    val s: String = h3.getValue() // 결과가 자동으로 올바른 타입으로 저장
    s eq "Chartreuse"
}

 

: T라는 플레이스 홀더지금은 알 수 없는 어떤 타입을 대신하며, 제네릭 클래스 안에서 일반 타입처럼 사용

 


 

[ Any ]

 

Any

: 코틀린에서의 유니버설 타입

: 유니버설 타입은 모든 타입의 부모 타입으로, 모든 타입의 인자를 허용

: 위의 예제에서 T 대신 Any를 사용하는 것도 가능함

: 간단한 경우 작동하지만, 구체적인 타입이 필요해질 경우 제대로 작동하지 않는 경우가 존재 (아래 예시 참고)

 

import atomictest.eq
class AnyHolder(private val value: Any) {
	fun getValue(): Any = value
}

class Dog {
	fun bark() = "Ruff!"
}

fun main() {
    val holder = AnyHolder(DogO)
    val any = holder.getValue()
    // any.bark() - 구체적이지 않기 때문에 컴파일 불가능
    
    // 아래와 같이 한 번 거쳐서 구체적인 타입 명시가 필요
    val genericHolder = GenericHolder(Dog())
    val dog = genericHolder.getValue()
    dog.bark() eq "Ruff!"
}

 

 


 

[ 제네릭 함수]

 

제네릭 함수

: 정의를 위해 부등호로 둘러싼 제네릭 타입 파라미터를 함수 이름 앞에 붙임

: 확장 함수를 사용하기 위해서는 수신 객체 앞에 제네릭 명세(괄호로 둘러싼 타입 파라미터 목록)를 위치시켜야 함

 

import atomictest.eq
// 제네릭 함수
fun <T> identity(arg: T): T = arg

// 확장 함수
fun <T> List<T>,first(): T {
	if (isEmpty())
		throw NoSuchElementException("Empty List")
    return this[0]
}

fun <T> List<T>.firstOrNull(): T?=
	if (isEmpty()) null else this[0]

fun main() {
	// 제네릭 함수 예시
	identity("Yellow") eq "Yellow"
    identity(1) eq 1
	val d: Dog = identity(Dog())
    d.bark() eq "Ruff!"

	// 확장 함수 예시
    listOf(1, 2Z 3).first() eq 1
    vali: Int? = listOf(1 z 2, 3) .firstOrNul() // 식별자 타입에 ?가 필요
    i eq 1
	val s: String? = listOf<String>().firstOrNul() // 식별자 타입에 ?가 필요
	s eq null
}

 

: 식별자 타입에 ?가 없을 경우 '널이 될 수 없는 타입을 기대하며, 널이 될 수 없는 타입이 와야 한다'는 오류가 발생하게 됨