Developing Myself Everyday
article thumbnail
Published 2023. 6. 29. 11:26
Generics (제네릭) Android/Kotlin

제네릭이란?


제네릭은 클래스, 인터페이스, 메서드에서 사용될 수 있으며, 타입 매개변수를 통해 일반적인 데이터 타입 대신 실제 타입을 지정할 수 있다. 이를 통해 코드를 보다 일반화할 수 있고, 재사용성을 높일 수 있다.

 

T 일반적으로 사용되는 타입 매개변수
E 요소(Element)의 타입을 나타내는 매개변수. 주로 컬렉션(Collection)에서 사용
K 맵(Map)에서 키(Key)의 타입을 나타내는 매개변수
V 맵(Map)에서 값(Value)의 타입을 나타내는 매개변수
N 숫자(Number) 타입을 나타내는 매개변수
R 반환(Return) 타입을 나타내는 매개변수입
S, U, V 등 여러 개의 타입 매개변수가 필요한 경우, 추가적으로 사용될 수 있는 일반적인 알파벳

 

제네릭을 사용하지 않았을 경우

우리가 String 문자열을 저장하는 리스트를 생성한다고 생각 해보자. 우리는 다음과 같이 코드를 작성할 것이다.

 

    val stringList: List<String> = listOf("Hello", "World") // 문자열을 저장하는 리스트 생성

 

이제 이 문자열의 요소를 출력해주는 메소드를 만들려고 한다. 

 

fun printStringList(list: List<String>) {
    for (item in list) {
        println(item)
    }
}

 

그렇다면 우리는 위와 같이 메소드를 작성해야 할 것이다. 이번엔 Int 문자열을 저장하는 리스트를 생성하고 이를 출력해주는 메소드를 만들어보겠다.

 

val integerList: List<Int> = listOf(10, 20) // 정수를 저장하는 리스트 생성
    
fun printIntList(list: List<Int>) {
    for (item in list) {
        println(item)
    }
}

 

위의 코드들을 한번에 나타내면 다음과 같다.

 

fun main() {
    val stringList: List<String> = listOf("Hello", "World") // 문자열을 저장하는 리스트 생성
    printStringList(stringList) // 문자열 리스트 출력

    val integerList: List<Int> = listOf(10, 20) // 정수를 저장하는 리스트 생성
    printIntList(integerList) // 정수 리스트 출력
}

fun printStringList(list: List<String>) {
    for (item in list) {
        println(item)
    }
}

fun printIntList(list: List<Int>) {
    for (item in list) {
        println(item)
    }
}

 

매우 불편한 코드이다. 2개의 메소드는 똑같은 로직을 수행하고 있다. 다만 파라미터의 타입만 다를 뿐이다. 불필요한 중복이 발생하고 있다. 이 때 사용할 수 있는것이 바로 제네릭이다.

 

 

 

제네릭을 사용했을 경우

fun main() {
    val stringList: List<String> = listOf("Hello", "World") // 문자열을 저장하는 리스트 생성
    printList(stringList) // 문자열 리스트 출력

    val integerList: List<Int> = listOf(10, 20) // 정수를 저장하는 리스트 생성
    printList(integerList) // 정수 리스트 출력
}

fun <T> printList(list: List<T>) {
    for (item in list) {
        println(item)
    }
}

 

제네릭을 사용한 코드이다. 중복을 한번에 해결한 것을 알 수 있다. 메소드 이름 왼쪽에 꺾쇠괄호가 오고 그 뒤에 데이터 유형의 자리표시자 이름, 오른쪽 꺽쇠괄호가 온다. 그럼 이 메소드는 List<T>의 파라미터를 받고, 리스트의 요소를 출력한다. T는 타입 매개변수로 실제 사용될 때 구체적인 타입으로 대체된다.

 


T(type의 약자)라는 일반 유형이 표시되거나, 클래스에 여러 일반 유형이 있는 경우 다른 대문자가 표시되는 경우가 많다. 하지만 이에 대한 명확한 규칙은 없으며 일반 유형에 더 구체적인 이름을 사용해도 된다.

 

 

 

클래스에서의 제네릭


클래스에서도 제네릭을 사용할 수 있다. 클래스에서는 아래와 같은 형식을 가진다.

 

 

클래스의 이름 오른쪽에 일반 유형 매개변수를 추가하고, 클래스의 속성에 추가한 일반 유형 매개변수를 지정할 수 있다. 이렇게 지정된 속성의 타입은 클래스를 인스턴스화 할 때 데이터 유형이 지정된다.

 

아래와 같은 예시를 봐보자.

 

class Question<T>(
    val questionText: String,
    val answer: T,
    val difficulty: String
)

 

위의 클래스에서는 일반 유형 매개변수를 T로 받고 answer의 타입을 T로 지정해주고 있다. 

 

fun main() {
    val question1 = Question<String>("Quoth the raven ___", "nevermore", "medium")
    val question2 = Question<Boolean>("The sky is green. True or false", false, "easy")
    val question3 = Question<Int>("How many days are there between full moons?", 28, "hard")
}

 

인스턴스를 생성할 때, 클래스의 이름 오른쪽에 answer에 들어갈 데이터 유형을 지정해주면 하나의 클래스로 3가지의 answer 타입을 갖는 코드를 만들 수 있다.

 

 

 

 

 

Reference

 

제네릭, 객체, 확장  |  Android Developers

Compose 코드에서 사용할 제네릭, enum 클래스, 데이터 클래스, 객체, 범위 함수와 같은 더 많은 Kotlin 개념을 대략적으로 알아봅니다.

developer.android.com

profile

Developing Myself Everyday

@배준형

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!