Dart 関数

関数はコードを整理する基本単位であり、再利用可能なロジックをカプセル化し、必要なときに呼び出せます。

Dart の関数システムは非常に柔軟で、名前付き引数、オプション引数、アロー関数、高階関数などの機能をサポートしています。


関数定義と戻り値

関数は戻り値の型、関数名、引数リスト、関数本体で構成されます。

例

// 簡単な関数を定義する
// String は戻り値の型、greet は関数名、(String name) は引数リスト
String greet(String name) {
  return 'こんにちは、$name!サンプル へようこそ。';
}

// 戻り値のない関数には void を使う
void printWelcome() {
  print('=== サンプル Dart チュートリアル ===');
}

void main() {
  printWelcome();

  // 関数を呼び出して戻り値を受け取る
  String message = greet('小明');
  print(message);
}
=== サンプル Dart 教程 ===
你好,小明!欢迎来到 サンプル。

戻り値

すべての関数には戻り値があります。

明示的な return がない場合、関数は暗黙的に null を返します(ただし、null 安全性の下では型の不一致を引き起こすため、通常は void を使って戻り値がないことを示します)。

例

// int 型を返す関数
int add(int a, int b) {
  return a + b;
}

// アロー構文で単一表現の関数を簡潔に書く
int multiply(int a, int b) => a * b;

// void は意味のある戻り値がないことを示す
void log(String msg) {
  print('[サンプル ログ] $msg');
}

void main() {
  print('3 + 5 = ${add(3, 5)}');
  print('3 × 5 = ${multiply(3, 5)}');
  log('関数の学習が完了しました');
}
3 + 5 = 8
3 × 5 = 15
[サンプル 日志] 函数学习完成

名前付き引数とオプション引数

Dart の引数は2種類に分けられます:必須引数(required)とオプション引数です。

オプション引数はさらに名前付き引数(named)と位置引数(positional)に分けられます。

名前付き引数(Named Parameters)

名前付き引数は波括弧 {} で囲み、呼び出し時に引数名で渡し、順序は任意です。

例

// 波括弧 {} 内は名前付き引数で、デフォルトでオプション
// required キーワードで必須にする
String createUser({
  required String name,    // 必須の名前付き引数
  int age = 0,             // オプション、デフォルト値は 0
  String? email,           // オプション、null を指定可能
  bool isVip = false,      // オプション、デフォルト値は false
}) {
  var info = 'ユーザー名: $name, 年齢: $age';
  if (email != null) {
    info += ', メール: $email';
  }
  if (isVip) {
    info += ' [VIPユーザー]';
  }
  return info;
}

void main() {
  // 名前付き引数:引数名で渡すため、順序は関係ない
  print(createUser(name: 'サンプル', age: 10));
  print(createUser(name: 'admin', email: '[email protected]', isVip: true));
  // 必須引数は省略できない
  // createUser(); // エラー:必須引数 name がありません
}
用户名: サンプル, 年龄: 10
用户名: admin, 邮箱: [email protected] [VIP用户]

位置引数(Positional Parameters)

位置引数は角括弧 [] で囲み、呼び出し時に順番に渡します。

例

// 角括弧 [] 内はオプションの位置引数
String buildUrl(String host, [String path = '/', int port = 80]) {
  return 'http://$host:$port$path';
}

void main() {
  // 必須引数のみ渡し、オプション引数はデフォルト値を使う
  print(buildUrl('www.example.com'));

  // 引数を2つ渡す
  print(buildUrl('www.example.com', '/dart'));

  // 引数を3つ渡す
  print(buildUrl('localhost', '/api', 8080));
}
http://www.example.com:80/
http://www.example.com:80/dart
http://localhost:8080/api

名前付き引数 vs 位置引数:どちらを選ぶか

シナリオ推奨方法理由
引数が多い(3つ以上)名前付き引数呼び出し時に引数名が自己文書化され、順序を間違えにくい
引数の意味が分かりにくい名前付き引数true/false のような値は用途が分からないので、名前を付けると明確になる
引数が少なく意味が明確位置引数add(1, 2) のように簡潔で明瞭
Flutter Widget の構築名前付き引数Flutter の標準スタイル

1つの関数で位置オプション引数と名前付きオプション引数を同時に使うことはできません。どちらか一方だけを選べます。


デフォルト引数値

オプション引数にはデフォルト値を指定でき、呼び出し側がその引数を渡さなかった場合にデフォルト値が使われます。

例

// すべてのオプション引数にデフォルト値が設定されている
String formatMessage(
  String content, {
  String prefix = '[サンプル]',
  String suffix = '',
  bool uppercase = false,
}) {
  var result = '$prefix $content $suffix';
  return uppercase ? result.toUpperCase() : result;
}

void main() {
  // すべてデフォルト値を使う
  print(formatMessage('Dart チュートリアルが更新されました'));

  // 一部のデフォルト値を上書き
  print(formatMessage('重要なお知らせ', prefix: '[お知らせ]', suffix: '!!!'));

  // デフォルト値を上書き + 大文字を有効にする
  print(formatMessage('error', prefix: '[エラー]', uppercase: true));
}
[サンプル] Dart 教程更新了
[公告] 重要通知 !!!
[错误] ERROR

デフォルト値はコンパイル時定数でなければなりません。つまり、デフォルト値は DateTime.now() や関数の戻り値(その関数が const の場合を除く)にすることはできません。実行時にデフォルト値を決定する必要がある場合は、関数本体内で ?? を使って処理できます。


アロー関数と匿名関数

関数本体が単一の式だけの場合、アロー構文(=>)で簡潔に書けます。

匿名関数は名前のない関数で、通常は他の関数に引数として渡されます。

例

void main() {
  var numbers = [1, 2, 3, 4, 5];

  // 匿名関数(完全な書き方)
  numbers.forEach((number) {
    print('サンプル 数値: $number');
  });

  print('---');

  // アロー関数(単一表現の簡略記法)
  var doubled = numbers.map((n) => n * 2);
  print('2倍: $doubled');

  // 関数定義におけるアロー関数の使用
  // 関数本体が return 文だけの場合は => で簡略化できる
  int square(int x) => x * x;
  print('5 の2乗: ${square(5)}');

  // 匿名関数を変数に格納する
  var sayHello = (String name) => 'こんにちは、$name!';
  print(sayHello('サンプル'));
}
サンプル 数字: 1
サンプル 数字: 2
サンプル 数字: 3
サンプル 数字: 4
サンプル 数字: 5
---
翻倍: (2, 4, 6, 8, 10)
5 的平方: 25
你好,サンプル!

高階関数とクロージャ

高階関数とは、関数を引数として受け取る、または関数を戻り値として返す関数のことです。

クロージャとは、関数がそのレキシカルスコープ内の変数にアクセスできることを指し、その関数がスコープの外で呼び出された場合でも同様です。

高階関数

例

// 高階関数:関数を引数として受け取る
List<int> filterList(
    List<int> items, bool Function(int) predicate) {
  return items.where(predicate).toList();
}

// 高階関数:関数を返す
Function makeMultiplier(int factor) {
  // クロージャを返す:外部の factor を記憶する
  return (int n) => n * factor;
}

void main() {
  var numbers = [10, 15, 20, 25, 30];

  // 匿名関数をフィルタ条件として渡す
  var bigNumbers = filterList(numbers, (n) => n > 18);
  print('サンプル 18より大きい数: $bigNumbers');

  // 返された関数を取得する
  var doubleIt = makeMultiplier(2);
  var tripleIt = makeMultiplier(3);

  print('5 × 2 = ${doubleIt(5)}');
  print('5 × 3 = ${tripleIt(5)}');
}
サンプル 大于18的数: [20, 25, 30]
5 × 2 = 10
5 × 3 = 15

クロージャ(Closure)

クロージャは関数オブジェクトで、そのレキシカルスコープ内の変数にアクセスできます。たとえ関数が元のスコープの外で呼び出されても同様です。

例

// クロージャの典型的な応用:カウンタを作成する
Function makeCounter() {
  int count = 0;  // この変数は返された関数に「捕捉」される
  return () {
    count++;
    return count;
  };
}

void main() {
  // counterA と counterB はそれぞれ独立した count を持つ
  var counterA = makeCounter();
  var counterB = makeCounter();

  print('サンプル カウンタ A: ${counterA()}');  // 1
  print('サンプル カウンタ A: ${counterA()}');  // 2
  print('サンプル カウンタ B: ${counterB()}');  // 1(独立したカウント)
  print('サンプル カウンタ A: ${counterA()}');  // 3
}
サンプル 计数器 A: 1
サンプル 计数器 A: 2
サンプル 计数器 B: 1
サンプル 计数器 A: 3

クロージャの鍵は、内部関数が外部関数の変数を「記憶」していることです。外部関数が実行を終えた後でも同様です。makeCounter() を呼び出すたびに、新しい count 変数とクロージャが作成されるため、複数のカウンタは互いに影響しません。

その他の拡張