제네릭

제네릭

정수를 담는 스택과 문자열을 담는 스택, 두 종류를 만들어야 한다고 상상해 볼게요. 같은 로직인데 타입만 다르다면 코드를 두 번 쓰는 건 낭비예요. 그럴 때 제네릭(generic)이 답이에요. 제네릭은 클래스나 트레이트가 타입을 매개변수로 받게 해주죠.

출처: Scala 3 Book — Generics

본문

제네릭 클래스(또는 트레이트)는 대괄호 [...] 안에 타입을 매개변수로 받아요. Scala의 관례는 그 타입 매개변수의 이름으로 A 같은 한 글자를 쓰는 거예요. 그 타입은 클래스 안에서 메서드 인스턴스 매개변수나 반환 타입으로 필요에 따라 사용할 수 있어요.

// here we declare the type parameter A
//          v
class Stack[A]:
  private var elements: List[A] = Nil
  //                         ^
  //  Here we refer to the type parameter
  //          v
  def push(x: A): Unit =
    elements = elements.prepended(x)
  def peek: A = elements.head
  def pop(): A =
    val currentTop = peek
    elements = elements.tail
    currentTop

Stack 클래스의 구현은 어떤 타입이든 매개변수로 받아요. 제네릭의 아름다움은 이제 Stack[Int], Stack[String] 등을 만들 수 있어서, Stack의 구현을 임의의 요소 타입에 재사용할 수 있다는 거예요.

Stack[Int]를 만들고 사용하는 방법은 이래요.

val stack = Stack[Int]
stack.push(1)
stack.push(2)
println(stack.pop())  // prints 2
println(stack.pop())  // prints 1

제네릭 타입으로 변량(variance)을 표현하는 방법에 대한 자세한 내용은 Variance 섹션을 참고해요.

더 알아보기