guricode

제네릭(Generic) 함수와 개념 본문

앱/Flutter&Dart

제네릭(Generic) 함수와 개념

agentrakugaki 2025. 6. 24. 15:52

제네릭(Generic)

  • Generic 은 "타입을 일반화해서 코드의 재사용성을 높이는 문법"입니다.
  • 타입에 따라 각각 함수를 만들 필요 없이, 하나의 함수나 클래스에 타입을 유연하게 지정할 수 있어요.
  • Dart에서는 <T> 형식으로 사용됩니다. (T는 타입의 약자, 마음대로 바꿔도 됨)

왜 필요한가

  • 타입마다 같은 로직의 함수나 클래스를 반복해서 만들 필요 없음
  • 더 안전한 코드 작성 가능 (타입 체크가 컴파일 타임에 이루어짐)
  • 타입 추론을 통해 가독성과 유지보수 향상

📌 기본 구조

T identity<T>(T value) {
  return value;
}

void main() {
  print(identity<int>(10));    // 10
  print(identity<String>('hi')); // hi
}
  • identity<T> : 입력 받은 타입 T 그대로 리턴하는 함수
  • identity<int> : T에 int를 넣은 버전

리스트 요소 타입에 따라 다르게 처리하는 함수

void printList<T>(List<T> items) {
  for (var item in items) {
    print('아이템: $item');
  }
}

void main() {
  printList<int>([1, 2, 3]);
  printList<String>(['a', 'b', 'c']);
}
  • List<T> : 어떤 타입의 리스트든 받아서 출력

다중 타입 제네릭 함수

void pairPrinter<K, V>(K key, V value) {
  print('Key: $key, Value: $value');
}

void main() {
  pairPrinter<String, int>('age', 30);
  pairPrinter<int, bool>(1, true);
}
  • <K, V> : key, value 각각의 타입을 일반화

 제네릭 타입 제약 (extends)

특정 타입만 허용하고 싶을 땐, extends를 이용해 타입을 제한할 수 있어요.

class Animal {
  void sound() => print('동물이 소리를 냅니다.');
}

class Dog extends Animal {
  @override
  void sound() => print('멍멍!');
}

void makeSound<T extends Animal>(T animal) {
  animal.sound();
}

void main() {
  makeSound(Dog()); // 멍멍!
  // makeSound(123); // 오류! Animal을 상속받지 않은 타입은 사용 불가
}
  • T extends Animal : T는 Animal을 상속받은 타입만 가능
  • 타입 안정성 향상에 매우 유용함

 정리

구분 설명

<T> 제네릭 타입 T 선언
<K, V> 여러 개 타입 선언 (예: Map key-value)
함수(T param) 제네릭 함수 구조
<T extends Class> 타입 제약 설정 (상속받은 클래스만 허용)

제네릭을 활용하면 코드가 훨씬 재사용 가능해지고, 타입 안정성도 보장됨