IMCAT.DEV
로그인 가입
Scala 중급 자료구조

Scala 제네릭 (Generics)

Scala 제네릭 (Generics) — 중급

제네릭 (Generics)

1. 도입 (왜 배워야 하는가)

제네릭은 Scala와 같은 현대 프로그래밍 언어에서 매우 중요한 개념입니다. 제네릭을 사용하면 코드의 재사용성을 높이고, 타입 안정성을 보장할 수 있습니다. 실무에서는 다양한 데이터 타입을 처리해야 하는 경우가 많기 때문에, 제네릭을 활용하지 않으면 코드의 중복이 발생할 수 있으며, 타입 관련 오류가 생길 위험이 있습니다. 제네릭을 모르면 특정 타입에 의존하는 코드를 작성하게 되어, 유지보수나 확장이 어려워질 수 있습니다. 예를 들어, 리스트를 사용하는 경우, 제네릭을 사용하지 않으면 Int, String 등 각기 다른 타입의 리스트를 별도로 구현해야 하는 불편함이 발생합니다.

2. 핵심 개념 설명

제네릭은 클래스나 메서드에서 사용할 데이터 타입을 일반화할 수 있게 해주는 기능입니다. 즉, 특정 타입을 지정하지 않고, 타입 매개변수를 사용하는 것입니다. 이를 통해 다양한 데이터 타입에 대해 동일한 로직을 적용할 수 있습니다.

비유

제네릭을 사용할 때는 마치 다용도 조리기구를 사용하는 것과 같습니다. 여러 재료를 넣어도 잘 조리할 수 있는 조리기구처럼, 제네릭을 사용하면 다양한 타입의 데이터를 처리할 수 있습니다.

관련 용어 정리

용어설명
타입 매개변수제네릭 클래스나 메서드에서 사용되는 변수
타입 안전성컴파일 타임에 타입 오류를 방지하는 기능

3. 기본 코드 예제

다음은 제네릭을 사용한 간단한 클래스 예제입니다.

class Box[T](value: T) {
  def getValue: T = value
}

val intBox = new Box(123) // Int 타입의 Box 생성
val stringBox = new Box("Hello") // String 타입의 Box 생성

println(intBox.getValue) // 123
println(stringBox.getValue) // Hello

실행 결과

123
Hello

코드 한 줄씩 동작 설명

  • class Box[T](value: T): 타입 매개변수 T를 가진 Box 클래스를 정의합니다. value는 T 타입입니다.
  • def getValue: T = value: getValue 메서드는 value를 반환합니다.
  • val intBox = new Box(123): Int 타입의 값을 가지는 Box 객체를 생성합니다.
  • val stringBox = new Box("Hello"): String 타입의 값을 가지는 Box 객체를 생성합니다.
  • println(intBox.getValue): intBox의 값을 출력합니다.
  • println(stringBox.getValue): stringBox의 값을 출력합니다.
  • 4. 실전 활용 예제

    예제 1: 쇼핑몰 장바구니

    쇼핑몰에서 장바구니를 구현할 때, 다양한 타입의 상품을 담을 수 있도록 제네릭을 사용할 수 있습니다.

    class ShoppingCart[T] {
      private var items: List[T] = List()
    
      def addItem(item: T): Unit = {
        items = item :: items
      }
    
      def getItems: List[T] = items
    }
    
    val cart = new ShoppingCart[String]()
    cart.addItem("사과")
    cart.addItem("바나나")
    
    println(cart.getItems) // List(바나나, 사과)

    예제 2: 데이터베이스 레포지토리

    제네릭을 활용하여 여러 타입의 데이터베이스 레포지토리를 만들 수 있습니다.

    trait Repository[T] {
      def save(item: T): Unit
      def find(id: String): Option[T]
    }
    
    class UserRepository extends Repository[User] {
      private var users: Map[String, User] = Map()
    
      def save(user: User): Unit = {
        users += (user.id -> user)
      }
    
      def find(id: String): Option[User] = {
        users.get(id)
      }
    }

    5. 흔한 실수와 주의사항

    실수 1: 타입 매개변수 사용을 잊는 경우

    잘못된 코드:

    class Box(value: Int) { / ... / }

    올바른 코드:

    class Box[T](value: T) { / ... / }

    실수 2: 타입 안정성 문제

    잘못된 코드:

    val mixedBox = new Box("Hello")
    mixedBox.getValue.asInstanceOf[Int] // Unchecked cast

    올바른 코드:

    val intBox = new Box(123)
    intBox.getValue // 타입에 안전하게 접근

    6. 정리

  • 제네릭은 코드의 재사용성과 타입 안전성을 높여줍니다.
  • 다양한 타입을 처리할 수 있는 유연성을 제공합니다.
  • 클래스와 메서드에서 타입 매개변수를 활용하여 구현합니다.
  • 다음에 학습하면 좋은 주제: 컬렉션과 고차 함수, 타입 클래스.