Menu
Coddy logo textTech

제네릭 제약 사항

Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 67번째.

때때로 제네릭 타입이 특정 기능을 갖추어야 할 필요가 있습니다. 예를 들어, 항목을 비교하거나 특정 속성에 접근하려는 경우, 제한이 없는 T는 이러한 기능이 존재함을 보장하지 않습니다. 제네릭 제약 조건(Generic constraints)을 사용하면 타입 매개변수를 특정 타입이나 그 하위 클래스로 제한할 수 있습니다.

extends 키워드를 사용하여 타입 매개변수를 제약하세요:

class NumberBox<T extends num> {
  T value;
  
  NumberBox(this.value);
  
  T doubled() => (value * 2) as T;
}

void main() {
  var intBox = NumberBox<int>(5);
  var doubleBox = NumberBox<double>(3.5);
  
  print(intBox.doubled());    // 10
  print(doubleBox.doubled()); // 7.0
  
  // var stringBox = NumberBox<String>('hi');  // 오류!
}

T extends num이기 때문에, 컴파일러는 value가 곱셈과 같은 숫자 연산을 지원한다는 것을 알 수 있습니다. 이러한 제약 조건이 없으면, value * 2를 호출하는 것은 실패하게 됩니다.

제약 조건은 직접 만든 클래스 계층 구조와도 함께 작동합니다:

abstract class Animal {
  String get name;
}

class Dog extends Animal {
  String get name => 'Dog';
}

class Shelter<T extends Animal> {
  List<T> animals = [];
  
  void add(T animal) => animals.add(animal);
  
  void printNames() {
    for (var animal in animals) {
      print(animal.name);  // 안전함: T는 'name'이 존재함을 보장함
    }
  }
}

제네릭 제약 조건은 제네릭의 유연성과 해당 타입에서 어떤 연산을 사용할 수 있는지 알 수 있는 안전성을 결합합니다.

challenge icon

챌린지

쉬움

숫자 타입에만 작동하는 통계 계산기를 만들어 봅시다! 숫자에만 사용할 수 있도록 보장하는 제약 조건이 있는 제네릭 클래스를 생성하여, 저장된 값에 대해 안전하게 수학적 연산을 수행할 수 있도록 할 것입니다.

코드를 두 개의 파일로 구성합니다:

  • stats.dart: 숫자 값 리스트를 관리하는 제네릭 Stats<T extends num> 클래스를 생성합니다:
    • 숫자를 저장하기 위한 values라는 이름의 List<T>
    • 제공된 값으로 리스트를 초기화하는 생성자
    • 모든 값의 합계를 num 타입으로 반환하는 sum() 메서드
    • 가장 작은 값을 T 타입으로 반환하는 min() 메서드
    • 가장 큰 값을 T 타입으로 반환하는 max() 메서드
    • 통계를 형식에 맞춰 표시하는 printStats() 메서드
  • main.dart: stats 파일을 임포트하고 서로 다른 숫자 타입을 사용하여 제약이 있는 제네릭 클래스를 시연합니다:
    • [10, 25, 5, 30, 15] 값을 가진 Stats<int>를 생성하고 printStats()를 호출합니다
    • 빈 줄을 하나 출력합니다
    • [3.5, 1.2, 4.8, 2.1] 값을 가진 Stats<double>를 생성하고 printStats()를 호출합니다

printStats() 메서드는 합계, 최솟값, 최댓값을 보여주는 세 줄을 출력해야 합니다. T extends num이기 때문에, 제약이 없는 제네릭 타입에서는 불가능했던 비교 연산자와 산술 연산을 값들에 대해 안전하게 사용할 수 있습니다.

예상 출력:

Sum: 85
Min: 5
Max: 30

Sum: 11.6
Min: 1.2
Max: 4.8

치트 시트

제네릭 제약 조건은 extends 키워드를 사용하여 타입 매개변수를 특정 타입이나 그 하위 클래스로 제한합니다:

class NumberBox<T extends num> {
  T value;
  
  NumberBox(this.value);
  
  T doubled() => (value * 2) as T;
}

T extends num을 사용하면, 컴파일러는 value가 곱셈과 같은 숫자 연산을 지원한다는 것을 알게 됩니다.

제약 조건은 사용자 정의 클래스 계층 구조에서도 작동합니다:

abstract class Animal {
  String get name;
}

class Shelter<T extends Animal> {
  List<T> animals = [];
  
  void printNames() {
    for (var animal in animals) {
      print(animal.name);  // 안전함: T는 'name'이 존재함을 보장합니다
    }
  }
}

제네릭 제약 조건은 타입 안전성과 사용 가능한 연산을 보장하면서 유연성을 제공합니다.

직접 해보기

import 'stats.dart';

void main() {
  // TODO: [10, 25, 5, 30, 15] 값을 가진 Stats<int>를 생성하고
  // printStats()를 호출하세요.
  
  // TODO: 빈 줄을 출력하세요.
  
  // TODO: [3.5, 1.2, 4.8, 2.1] 값을 가진 Stats<double>을 생성하고
  // printStats()를 호출하세요.
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨