본문 바로가기
개발언어/Kotlin : 코틀린

코틀린 익히기 8 - 제네릭

by 개발자D 2023. 9. 28.

제네릭

제네릭

제네릭은 jdk 1.5부터 도입되었습니다. 타입을 미리 지정하지 않고 컴파일할 때 체크할 수 있도록 하는 기능입니다. 미리 타입을 지정해 두면 타입을 형변환하거나 체크해야 하는 번거로움이 있습니다. 제네릭을 사용하면 이와 같은 부담에서 벗어날 수 있습니다. 제네릭은 클래스와 메서드에 선언할 수 있습니다. 어떻게 선언하고 사용하는지 살펴봅시다.

 

제네릭에서 사용하는 형식 매개변수 관례

E 요소
K
N 숫자
T 형식
V

 

1. 제네릭 클래스

제네릭 클래스는 형식 매개변수를 사용하는 클래스입니다.

일반 클래스 Generic

class Generic (var property: Any?) { // 주 생성자
    
    var variabe: Any? = null // 프로퍼티
    
    constructor(property2: Any?, property3: Any?) : this(property2) {} // 부 생성자
    
    fun setProperty(variable: Any?): Any? { // 메서드
        variabe = variable
    }
}

지금까지 봐왔던 일반적인 클래스입니다. 주 생성자, 프로퍼티, 부 생성자, 메서드 매개변수, 반환값의 타입이 Any?로 고정되어 있습니다. 

제네릭 클래스로 변경한 클래스 Generic

class Generic<T> (var property: T) {

    var variabe: T? = null

    constructor(property2: T, property3: T) : this(property2) {}

    fun setProperty(property: T) : T {
        this.property = property
        return property
    }
}

Any? 였던 타입이 T로 변경되었습니다. T 자료형은 클래스 내의 주 생성자, 부 생성자에, 메서드에 사용할 수 있습니다.

프로퍼티는 기본적으로 초기화되어 있어야 하기에 생성자로 초기화되지 않는 프로퍼티의 자료형으로는 잘 사용하지 않습니다.

T 자료형은 클래스를 선언하는 시점이 아닌 인스턴스를 생성하는 시점에 지정됩니다.

 

제네릭 클래스의 인스턴스 생성 예시

<> 안에 지정하고 싶은 자료형을 입력합니다.

var generic = Generic<String>("hi generic")

 


다형성

이전에 공부했던 다형성을 기억하시나요? 객체 지향 프로그래밍의 특징 중 하위 클래스의 인스턴스가 상위 클래스의 참조변수로 선언될 수 있었던 특징입니다.

open class Vehicle {
   ...
}

open class Car : Vehicle() {
   ...
}

class Truck : Car() {
   ...
}

// Vehicle > Car > Truck

var vehicle : Vehicle = Vehicle() // 가능
var car : Car = Car() // 가능
var truck : Truck = Truck() // 가능
var carTruck : Car = Truck() // 가능
var vehicleTruck : Vehicle = Truck() // 가능
var truckCar : Truck = Car() // 불가능

 

제네릭에서는 이러한 다형성에 제한이 있습니다.

var car: Generic<Car> = Generic<Car>() // O
var car: Generic<Car> = Generic() // 으로 생략 가능

var car: Generic<Car> = Generic<Truck>() // X

 

우선, 대입된 타입이 상속관계일지라도 다형성이 성립되지 않습니다. 참조변수와 생성자에 대입된 타입이 일치해야 합니다.

 

open class Generic<T> {
	...
}

class Generic2<T> : Generic<T>() {
    ...
}

var car: Generic<Int> = Generic2<Int>() // O

제네릭 클래스 자체가 상속관계에 있고 대입된 타입이 같은 것은 다형성이 성립됩니다.

 

 


2. 제네릭 메서드

메서드가 호출될 때 컴파일러가 자료형을 추론합니다. 반환 자료형과 매개변수 자료형에 사용할 수 있습니다.

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

 

제네릭 함수의 호출 예시

<> 안에 지정하고 싶은 자료형을 입력합니다.

method<Int>(1, 2, {a, b -> a + b})

 


대입할 타입의 제한

를 통해 대입할 자료형을 특정 클래스를 상속받는 클래스 혹은 특정 인터페이스를 구현하는 클래스로 제한할 수 있습니다.

또한, null이 허용되지 않는 자료형을 입력해 null 가능 자료형의 입력을 제한할 수 있습니다.

다수의 조건으로 제한하려면 where 키워드를 사용합니다. where 키워드 뒤의 조건은 &로 묶입니다 따라서 모두 해당하는 경우로 제한됩니다.

class GenericNumber<T: Number> {}
fun main() {
	val obj1 = GenericNumber<String>() // 오류
}
----------------------
class GenericNotNull<T: Any> {}
fun main() {
	val obj2 = GenericNotNull<Int?>() // 오류
}
----------------------
interface InterfaceA 
interface InterfaceB

class ImplA: InterfaceA, InterfaceB
class ImplB: InterfaceB

class GenericTwo<T> where T:InterfaceA, T:InterfaceB {}
fun main() {
    val obj3 = GenericTwo<ImplA>() 
    val obj4 = GenericTwo<ImplB>() // 오류
}

이번 글에서는 코틀린의 제네릭에 대해 살펴봤습니다. 코틀린의 제네릭은 자바의 제네릭과 크게 다르지 않기 때문에 자바를 공부했던 분이시라면 쉽게 이해하셨으리라 생각합니다. 다음 글에서는 제네릭과 관련된 in & out 키워드를 사용하는 변성에 대해 설명드려보도록 하겠습니다. 읽어주셔서 감사합니다.

 

코틀린 익히기 9 - 변성 Variance

코틀린 익히기 8 - 제네릭 제네릭 제네릭은 jdk 1.5부터 도입되었습니다. 타입을 미리 지정하지 않고 컴파일할 때 체크할 수 있도록 하는 기능입니다. 미리 타입을 지정해 두면 타입을 형변환하거

devdharu.tistory.com