이 포스트는 아래의 Android 공식 홈페이지에서 제공하는 Codelab을 기반으로 작성되었습니다.
https://developer.android.com/codelabs/basic-android-kotlin-compose-generics?hl=ko#0
온라인 퀴즈 앱을 작성한다고 가정할 때. 퀴즈의 종류는 총 3가지가 있다.
1. 빈 칸 채우기 질문(서술형): 답변은 [String]형태
2. True Or False 질문: 답변은 [Boolean]
3. 수학 문제: 답변은 [Int]
또한 각 퀴즈 질문에는 난이도가 "easy", "medium", "hard" 셋 중 하나의 문자열로 표시된다.
이때 각 문제 클래스를 다음과 같이 정의할 수 있다.
class FillInTheBlankQuestion(
val questionText: String,
val answer: String,
val difficulty: String
)
class TrueOrFalseQuestion(
val questionText: String,
val answer: Boolean,
val difficulty: String
)
class NumericQuestion(
val questionText: String,
val answer: Int,
val difficulty: String
)
세 클래스 모두 [questionText], [answer], [difficulty] 같은 공통된 변수를 포함하고 있다. 유일한 차이는 [answer] 속성의 데이터 타입.
[questionText], [difficulty]와 같은 공통된 변수를 상위 클래스로 만들고 각 서브 클래스가 [answer] 속성을 정의하게 하면 반복을 피할 수 있다.
하지만 이는 결국 비슷한 문제와 부딪히게 된다.
새로운 유형의 질문을 추가할 때마다 [answer] 속성이 계속 추가되어야한다. 유일한 차이점은 데이터 유형 하나일 뿐. 또한 상위 클래스인 [Question] 클래스에 [answer] 속성이 없는 것도 이상하다.
따라서 이럴때는 서브 클래스를 활용하는게 정답이 아니다.
Kotlin에는 일반 유형(Generic data type)이라는 것을 제공하며, 이를 통해 특정 사용 사례에 따라 데이터 유형이 다를 수 있는 단일 속성을 지정할 수 있다.
위의 예시에서 각 데이터 유형의 답변 속성을 Int, String, Boolean 별로 정의하는 대신 질문을 나타내는 단일 클래스 [Question]을 만들고 [answer] 속성의 데이터 유형에 대해 placeholder(자리표시자) 이름을 사용할 수 있다.
실제 데이터 유형(String, Int, Boolean 등)은 이 [Question] 클래스가 인스턴스화 될 때 지정된다.
Generic Data Type은 클래스를 인스턴스화할 때 제공되므로 클래스 서명의 일부로 정의해야한다.
Generic Type에서 사용되는 데이터 타입은 클래스가 인스턴스화될 때 <>로 묶여 매개변수로 전달된다.
이제 위의 예제를 Generic Data Type을 활용해서 다시 구현해보자.
코드를 리팩토링하여 [Question]이라는 단일 클래스를 사용한다.
class Question<T>() {
val questionText: String,
val answer: T,
val difficulty: String
}
fun main() {
val question1 = Question<String>("Text1", "answer1", "easy")
val question2 = Question<Boolean>("Text2", false, "medium")
val question3 = Question<Int>("Text3", 14, "hard")
}