Dartにおいて、関数は単なる実行コードのまとまりではなく、真の 第一級オブジェクト です。
関数を変数に代入したり、他の関数の引数として渡したり、戻り値として返したりすることができます。 また、FlutterのWidgetツリー構築において必須となる「名前付き引数」や「アロー構文」、状態をカプセル化する「クロージャ」など、Dartの関数表現は非常に洗練されています。
この章では、Dartの関数の定義方法、パラメータの柔軟な指定方法、そしてクロージャの仕組みを詳しく学びます。
Dartの関数は 第一級オブジェクト であり、Function 型の値として変数に代入したり、高階関数の引数として渡すことができます。
まずは標準的な関数定義を見てみましょう。
int add(int a, int b) {
return a + b;
}
void main() {
int result = add(3, 5);
print('3 + 5 = $result');
}dart run function_basics.dart3 + 5 = 8
=>)と関数オブジェクト関数本体が単一の式(Expression)のみで構成される場合、波括弧 { return ...; } の代わりに =>(アロー構文) を使って簡潔に記述できます。
また、関数を変数に格納して利用することもできます。
// アロー構文による定義
int multiply(int a, int b) => a * b;
void main() {
print('アロー関数: ${multiply(4, 5)}');
// 変数に関数を代入
int Function(int, int) op = multiply;
print('変数経由の呼び出し: ${op(10, 20)}');
}dart run arrow_syntax.dartアロー関数: 20 変数経由の呼び出し: 200
Dartの関数のパラメータには、大きく分けて以下の2つの分類があります。
{}) と 位置指定引数([]) があります。// 必須の位置引数
void greet(String name, String message) {
print('$nameさん、$message');
}
void main() {
greet('Alice', 'こんにちは');
}dart run parameter_types.dartAliceさん、こんにちは
{})と required引数を {} で囲むと、呼び出し側で引数名を明示して渡すことができるようになります。引数の順番は自由です。
required を付けると、名前付き引数でありながら省略不可(必須)にできます。// required で必須、デフォルト値付きで省略可能
void createUser({
required String username,
String role = 'member',
int? age,
}) {
print('ユーザー: $username, 権限: $role, 年齢: ${age ?? "未設定"}');
}
void main() {
createUser(username: 'Alice');
createUser(role: 'admin', username: 'Bob', age: 30);
}dart run named_parameters.dartユーザー: Alice, 権限: member, 年齢: 未設定 ユーザー: Bob, 権限: admin, 年齢: 30
[])引数を [] で囲むと、順番通りのオプショナル引数を定義できます。
String formatMessage(String from, String msg, [String? appName = 'MyChat']) {
return '[$appName] $from: $msg';
}
void main() {
print(formatMessage('Alice', 'Hello'));
print(formatMessage('Bob', 'Hi', 'FlutterApp'));
}dart run optional_positional.dart[MyChat] Alice: Hello [FlutterApp] Bob: Hi
Dartでは、名前を持たない 無名関数(ラムダ式) を定義してコールバックとして渡したり、外側のスコープの変数を保持する クロージャ を作成できます。
関数名を付けずに定義する関数を 無名関数 と呼びます。イベントハンドラやコレクションの操作(第4章参照)に頻繁に利用されます。
void main() {
final fruits = ['apple', 'banana', 'orange'];
// (item) { ... } という無名関数を forEach に渡す
fruits.forEach((fruit) {
print('フルーツ: $fruit');
});
// アロー構文を用いた無名関数
fruits.forEach((fruit) => print('UPPER: ${fruit.toUpperCase()}'));
}dart run anonymous_functions.dartフルーツ: apple フルーツ: banana フルーツ: orange UPPER: APPLE UPPER: BANANA UPPER: ORANGE
クロージャ とは、関数が定義されたスコープの外側の変数を「キャプチャ(保持)」し、関数が別のスコープで実行されてもその変数にアクセス・変更できる仕組みです。
// カウンター関数を生成する高階関数
Function makeCounter() {
int count = 0; // クロージャによってキャプチャされるローカル変数
return () {
count++;
return count;
};
}
void main() {
final counter1 = makeCounter();
final counter2 = makeCounter();
print('counter1: ${counter1()}'); // 1
print('counter1: ${counter1()}'); // 2
print('counter2: ${counter2()}'); // 1 (独立した状態を持つ)
print('counter1: ${counter1()}'); // 3
}dart run closures.dartcounter1: 1 counter1: 2 counter2: 1 counter1: 3
この章では、Dartの関数システムと表現力について学びました。
=>): 単一式を返す関数を簡潔に書ける。{}): 呼び出し時に引数名を明示でき、required で必須指定、デフォルト値設定が可能。[]): 順番に従って省略可能なオプショナル引数を定義できる。次の 第4章 では、日常の開発で多用する List, Set, Map などのコレクションと強力なコレクション構文について学びます。
商品の税込金額を計算する関数 calculateTotal を作成してください。
calculateTotal は以下の引数を持ちます。
price: 商品本体価格(int, 必須の名前付き引数 required)taxRate: 消費税率(double, デフォルト値 0.10 の名前付き引数)discount: 割引額(int, デフォルト値 0 の名前付き引数)~/ 1 または .toInt())を返す。main() 関数で、デフォルト税率での計算と、割引・軽減税率(例: taxRate: 0.08)を指定した計算を実行して結果を出力する。// ここに関数を定義してください
void main() {
// ここで動作確認を行ってください
}dart run practice3_1.dart指定したステップ数ずつ増加するカスタムカウンターを生成する関数 createStepCounter を作成してください。
int Function() createStepCounter(int step) を定義する。0 の変数 current を保持し、呼び出されるたびに step ずつ加算してその値を返す無名関数を返却する。main() でステップ数 5 のカウンターを作成し、3回呼び出して 5, 10, 15 と出力されることを確認する。// ここに関数を定義してください
void main() {
// ここで動作確認を行ってください
}dart run practice3_2.dart