Menu
Coddy logo textTech

拡張メソッド

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 83/110。

Dartの組み込みの String 型や int 型のように、自分が所有していないクラスに機能を追加したい場合があります。それらのソースコードを変更することはできませんし、継承は final クラスに対しては役に立ちません。そこで 拡張メソッド の出番です。

エクステンション(拡張)を使用すると、既存の型を変更したりサブクラスを作成したりすることなく、新しいメソッドを追加できます:

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 です。内部では、通常のクラスと同じようにメソッドやゲッターを定義します。一度定義すると、これらのメソッドはその型のすべてのインスタンスに、まるであらかじめ組み込まれているかのように表示されます。

拡張機能は、独自のクラスやジェネリック型を含む、あらゆる型に対して動作します。

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
}

エクステンション内では、this はメソッドが呼び出されたインスタンスを指すことに注意してください。エクステンションは、コードの整理と可読性を保ちながら、既存の型を拡張するためのクリーンな方法です。

challenge icon

チャレンジ

簡単

拡張メソッドを使用して、Dartの組み込み型に便利な機能を追加しましょう!String型とList型に役立つメソッドを追加する拡張機能を作成し、それらをメインファイルで使用します。

コードを2つのファイルに整理します:

  • extensions.dart: 既存の型に新しい機能を追加する2つの拡張機能を作成します:
    • Stringに対するStringUtilsという名前の拡張機能:
      • 文字列内の母音(a, e, i, o, u - 大文字小文字を区別しない)の数を返すメソッド countVowels()
      • 各単語(スペースで区切られたもの)の最初の文字を結合し、大文字にして返すゲッター initials。例えば、"hello world""HW" になります。
    • List<T>に対するListUtils<T>という名前の拡張機能:
      • 指定されたインデックスに要素が存在すればそれを返し、インデックスが範囲外であれば null を返すメソッド safeGet(int index)
      • リストに複数の要素がある場合に true を返すゲッター hasMultiple
  • 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腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン