Menu
Coddy logo textTech

확장 메서드

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

때때로 Dart의 내장 String 또는 int 타입과 같이 직접 소유하지 않은 클래스에 기능을 추가하고 싶을 때가 있습니다. 이러한 클래스의 소스 코드는 수정할 수 없으며, 상속은 final 클래스에 대해 도움이 되지 않습니다. 이럴 때 확장 메서드(extension methods)가 필요합니다.

익스텐션(Extensions)을 사용하면 기존 타입을 수정하거나 서브클래스를 생성하지 않고도 새로운 메서드를 추가할 수 있습니다:

extension StringExtras on String {
  String reverse() {
    return split('').reversed.join('');
  }
  
  bool get isPalindrome {
    var cleaned = toLowerCase().replaceAll(' ', '');
    return cleaned == cleaned.split('').reversed.join('');
  }
}

void main() {
  print('hello'.reverse());        // olleh
  print('radar'.isPalindrome);     // true
}

구문은 extension ExtensionName on Type입니다. 내부에서는 일반 클래스에서와 마찬가지로 메서드와 게터를 정의합니다. 일단 정의되면, 이러한 메서드들은 마치 내장된 것처럼 해당 타입의 모든 인스턴스에 나타납니다.

확장(Extensions)은 사용자 정의 클래스와 제네릭 타입을 포함하여 모든 타입에서 작동합니다:

extension IntExtras on int {
  int squared() => this * this;
}

extension ListExtras<T> on List<T> {
  T? get secondOrNull => length >= 2 ? this[1] : null;
}

void main() {
  print(5.squared());                    // 25
  print([1, 2, 3].secondOrNull);         // 2
}

확장(extension) 내부에서 this는 메서드가 호출된 인스턴스를 가리킨다는 점에 유의하세요. 확장은 코드를 체계적이고 가독성 있게 유지하면서 기존 타입을 개선하는 깔끔한 방법입니다.

challenge icon

챌린지

쉬움

확장 메서드(extension methods)를 사용하여 Dart의 기본 제공 타입에 유용한 기능을 추가해 봅시다! StringList 타입에 유용한 메서드를 추가하는 확장을 만들고, 이를 메인 파일에서 사용하게 됩니다.

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

  • extensions.dart: 기존 타입에 새로운 기능을 추가하는 두 개의 확장을 만듭니다:
    • String 타입에 대한 StringUtils라는 이름의 확장:
      • 문자열 내의 모음(a, e, i, o, u - 대소문자 구분 없음) 개수를 반환하는 countVowels() 메서드
      • 각 단어(공백으로 구분)의 첫 글자를 대문자로 합쳐서 반환하는 initials 게터(getter). 예를 들어, "hello world""HW"가 됩니다.
    • List<T> 타입에 대한 ListUtils<T>라는 이름의 확장:
      • 인덱스가 존재하면 해당 요소를 반환하고, 인덱스가 범위를 벗어나면 null을 반환하는 safeGet(int index) 메서드
      • 리스트에 요소가 두 개 이상 있으면 true를 반환하는 hasMultiple 게터(getter)
  • main.dart: 작성한 확장을 임포트하고 실제로 작동하는 모습을 보여줍니다:
    • "Object Oriented Programming" 문자열을 생성하고, 모음 개수를 Vowels: [count] 형식으로 출력합니다.
    • 동일한 문자열의 이니셜을 Initials: [initials] 형식으로 출력합니다.
    • 정수 리스트 [10, 20, 30]를 생성합니다.
    • safeGet(1)의 결과를 Element at 1: [value] 형식으로 출력합니다.
    • safeGet(5)의 결과를 Element at 5: [value] 형식으로 출력합니다.
    • 리스트에 여러 요소가 있는지 여부를 Has multiple: [true/false] 형식으로 출력합니다.

확장 메서드 내부에서 this는 메서드가 호출된 인스턴스를 가리킨다는 점을 기억하세요. 여러분이 만든 확장을 통해 이러한 타입들이 마치 해당 메서드들을 기본적으로 내장하고 있는 것처럼 느껴지게 될 것입니다!

예상 출력 결과:

Vowels: 9
Initials: OOP
Element at 1: 20
Element at 5: null
Has multiple: true

치트 시트

확장 메서드를 사용하면 기존 유형의 소스 코드를 수정하거나 상속을 사용하지 않고도 해당 유형에 새로운 기능을 추가할 수 있습니다.

확장을 생성하기 위한 구문은 extension ExtensionName on Type입니다:

extension StringExtras on String {
  String reverse() {
    return split('').reversed.join('');
  }
  
  bool get isPalindrome {
    var cleaned = toLowerCase().replaceAll(' ', '');
    return cleaned == cleaned.split('').reversed.join('');
  }
}

일단 정의되면, 확장 메서드는 마치 내장된 메서드인 것처럼 해당 유형의 인스턴스에서 호출할 수 있습니다:

print('hello'.reverse());        // olleh
print('radar'.isPalindrome);     // true

확장은 내장 유형, 사용자 정의 클래스, 제네릭 유형을 포함한 모든 유형에서 작동합니다:

extension IntExtras on int {
  int squared() => this * this;
}

extension ListExtras<T> on List<T> {
  T? get secondOrNull => length >= 2 ? this[1] : null;
}

print(5.squared());                    // 25
print([1, 2, 3].secondOrNull);         // 2

확장 내부에서 this는 메서드가 호출되는 인스턴스를 가리킵니다.

직접 해보기

import 'extensions.dart';

void main() {
  // 문자열 생성
  String text = "Object Oriented Programming";

  // TODO: countVowels()를 사용하여 모음 개수 출력
  // 형식: Vowels: [count]

  // TODO: initials 게터를 사용하여 이니셜 출력
  // 형식: Initials: [initials]

  // 정수 리스트 생성
  List<int> numbers = [10, 20, 30];

  // TODO: safeGet(1)의 결과 출력
  // 형식: Element at 1: [value]

  // TODO: safeGet(5)의 결과 출력
  // 형식: Element at 5: [value]

  // TODO: 리스트에 여러 요소가 있는지 여부 출력
  // 형식: Has multiple: [true/false]
}
quiz icon실력 점검

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

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