目次

関数

Dart は真のオブジェクト指向言語であるため、関数でさえオブジェクトであり、Function という型を持ちます。これは、関数を変数に代入したり、他の関数の引数として渡したりできることを意味します。また、Dart クラスのインスタンスを関数であるかのように呼び出すこともできます。詳細については、呼び出し可能オブジェクトを参照してください。

以下は関数を実装する例です

dart
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

Effective Dart ではパブリック API の型アノテーションが推奨されていますが、型を省略しても関数は機能します

dart
isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

1 つの式のみを含む関数には、短縮構文を使用できます

dart
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

=> expr 構文は、{ return expr; } の短縮形です。=> 表記は、アロー構文と呼ばれることがあります。

パラメーター

#

関数は、任意の数の必須の位置パラメーターを持つことができます。これらに続けて、名前付きパラメーターまたはオプションの位置パラメーター (両方は不可) を指定できます。

関数に引数を渡すとき、または関数パラメーターを定義するときに、末尾のコンマを使用できます。

名前付きパラメーター

#

名前付きパラメーターは、required として明示的にマークされない限り、オプションです。

関数を定義するときは、{param1, param2, …} を使用して名前付きパラメーターを指定します。デフォルト値を指定しない場合、または名前付きパラメーターを required としてマークしない場合、それらのデフォルト値は null になるため、それらの型は null 許容である必要があります。

dart
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool? bold, bool? hidden}) {...}

関数を呼び出すときは、paramName: value を使用して名前付き引数を指定できます。例:

dart
enableFlags(bold: true, hidden: false);

null 以外の名前付きパラメーターのデフォルト値を定義するには、= を使用してデフォルト値を指定します。指定された値は、コンパイル時定数である必要があります。例:

dart
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold will be true; hidden will be false.
enableFlags(bold: true);

代わりに、名前付きパラメーターを必須にして、呼び出し元がパラメーターの値を指定するように強制したい場合は、required で注釈を付けます

dart
const Scrollbar({super.key, required Widget child});

child 引数を指定せずに Scrollbar を作成しようとすると、アナライザーが問題を報告します。

位置引数を最初に配置したい場合もありますが、Dart では必須ではありません。Dart では、API に合わせて、引数リストの任意の場所に名前付き引数を配置できます。

dart
repeat(times: 2, () {
  ...
});

オプションの位置パラメーター

#

関数パラメーターのセットを [] で囲むと、それらはオプションの位置パラメーターとしてマークされます。デフォルト値を指定しない場合、それらのデフォルト値は null になるため、それらの型は null 許容である必要があります

dart
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

以下は、オプションのパラメーターなしでこの関数を呼び出す例です

dart
assert(say('Bob', 'Howdy') == 'Bob says Howdy');

以下は、3 番目のパラメーターを指定してこの関数を呼び出す例です

dart
assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');

null 以外のオプションの位置パラメーターのデフォルト値を定義するには、= を使用してデフォルト値を指定します。指定された値は、コンパイル時定数である必要があります。例:

dart
String say(String from, String msg, [String device = 'carrier pigeon']) {
  var result = '$from says $msg with a $device';
  return result;
}

assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

main() 関数

#

すべてのアプリには、アプリへのエントリポイントとして機能するトップレベルの main() 関数が必要です。main() 関数は void を返し、引数のためのオプションの List<String> パラメーターを持ちます。

以下は単純な main() 関数です

dart
void main() {
  print('Hello, World!');
}

以下は、引数を取るコマンドラインアプリの main() 関数の例です

args.dart
dart
// Run the app like this: dart run args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');
}

args ライブラリを使用して、コマンドライン引数を定義および解析できます。

第一級オブジェクトとしての関数

#

関数を別の関数のパラメーターとして渡すことができます。例:

dart
void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

次のように、関数を変数に代入することもできます

dart
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

この例では、匿名関数を使用しています。詳細については、次のセクションで説明します。

関数型

#

関数型として知られる関数の型を指定できます。関数型は、関数宣言ヘッダーで関数名をキーワード Function に置き換えることによって取得されます。さらに、位置パラメーターの名前を省略することは許可されていますが、名前付きパラメーターの名前を省略することはできません。例:

dart
void greet(String name, {String greeting = 'Hello'}) =>
    print('$greeting $name!');

// Store `greet` in a variable and call it.
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');

匿名関数

#

main()printElement() のように、ほとんどの関数には名前を付けますが、名前のない関数を作成することもできます。これらの関数は、匿名関数ラムダ、またはクロージャと呼ばれます。

匿名関数は、名前付き関数に似ています。その理由は、以下のものがあるからです

  • 0 個以上のパラメーター (コンマ区切り)
  • かっこで囲まれたオプションの型アノテーション

次のコードブロックには、関数の本体が含まれています

dart
([[Type] param1[, ...]]) {
  codeBlock;
}

次の例では、型指定されていないパラメーター item を持つ匿名関数を定義します。匿名関数は、それを map 関数に渡します。リスト内の各項目に対して呼び出される map 関数は、各文字列を大文字に変換します。次に、forEach に渡される匿名関数は、変換された各文字列とその長さを出力します。

dart
const list = ['apples', 'bananas', 'oranges'];

var uppercaseList = list.map((item) {
  return item.toUpperCase();
}).toList();
// Convert to list after mapping

for (var item in uppercaseList) {
  print('$item: ${item.length}');
}

コードを実行するには、[実行] をクリックします。

void main() {
  const list = ['apples', 'bananas', 'oranges'];

  var uppercaseList = list.map((item) {
    return item.toUpperCase();
  }).toList();
  // Convert to list after mapping

  for (var item in uppercaseList) {
    print('$item: ${item.length}');
  }
}

関数に 1 つの式または return ステートメントのみが含まれている場合は、アロー表記を使用して短縮できます。次の行を DartPad に貼り付けて、[実行] をクリックして、機能的に同等であることを確認します。

dart
var uppercaseList = list.map((item) => item.toUpperCase()).toList();
uppercaseList.forEach((item) => print('$item: ${item.length}'));

レキシカルスコープ

#

Dart は、コードのレイアウトに基づいて変数のスコープを決定します。この機能を持つプログラミング言語は、レキシカルスコープ言語と呼ばれます。「外側の中括弧をたどる」ことで、変数がスコープ内にあるかどうかを確認できます。

例: 各スコープレベルに変数がある一連のネストされた関数

dart
bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

nestedFunction() メソッドは、トップレベルまでのすべてのレベルの変数を使用できます。

レキシカルクロージャ

#

関数がスコープ外にあるときに、レキシカルスコープ内の変数にアクセスできる関数オブジェクトは、クロージャと呼ばれます。

関数は、外側のスコープで定義された変数をクロージャとして取り込むことができます。次の例では、makeAdder()は変数addByをキャプチャします。返された関数がどこへ行こうとも、addByを覚えています。

dart
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(int addBy) {
  return (int i) => addBy + i;
}

void main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

ティアオフ

#

関数、メソッド、または名前付きコンストラクターを括弧なしで参照すると、Dartはティアオフを作成します。これは、関数と同じパラメーターを受け取り、呼び出されると基になる関数を呼び出すクロージャです。コードが、クロージャが受け入れるのと同じパラメーターで名前付き関数を呼び出すクロージャを必要とする場合、ラムダで呼び出しをラップしないでください。ティアオフを使用してください。

dart
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
gooddart
// Function tear-off
charCodes.forEach(print);

// Method tear-off
charCodes.forEach(buffer.write);
baddart
// Function lambda
charCodes.forEach((code) {
  print(code);
});

// Method lambda
charCodes.forEach((code) {
  buffer.write(code);
});

関数の等価性をテストする

#

以下は、トップレベル関数、静的メソッド、およびインスタンスメソッドの等価性をテストする例です。

dart
void foo() {} // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {} // An instance method
}

void main() {
  Function x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = A(); // Instance #1 of A
  var w = A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}

戻り値

#

すべての関数は値を返します。戻り値が指定されていない場合、ステートメントreturn null;が関数本体に暗黙的に追加されます。

dart
foo() {}

assert(foo() == null);

関数で複数の値を返すには、レコードに値を集約します。

dart
(String, int) foo() {
  return ('something', 42);
}

ジェネレーター

#

値のシーケンスを遅延生成する必要がある場合は、ジェネレーター関数の使用を検討してください。Dartには、2種類のジェネレーター関数の組み込みサポートがあります。

  • 同期ジェネレーター:Iterableオブジェクトを返します。
  • 非同期ジェネレーター:Streamオブジェクトを返します。

同期ジェネレーター関数を実装するには、関数本体をsync*としてマークし、値を配信するためにyieldステートメントを使用します。

dart
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

非同期ジェネレーター関数を実装するには、関数本体をasync*としてマークし、値を配信するためにyieldステートメントを使用します。

dart
Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

ジェネレーターが再帰的な場合は、yield*を使用することでパフォーマンスを向上させることができます。

dart
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

外部関数

#

外部関数は、その本体が宣言とは別に実装されている関数です。次のように、関数宣言の前にexternalキーワードを含めます。

dart
external void someFunc(int i);

外部関数の実装は、別のDartライブラリから、またはより一般的には、別の言語から取得できます。相互運用コンテキストでは、externalは外部関数または値の型情報を導入し、Dartで使用できるようにします。実装と使用法はプラットフォームに大きく依存するため、たとえば、CまたはJavaScriptの相互運用ドキュメントをチェックして、詳細を確認してください。

外部関数は、トップレベル関数、インスタンスメソッドゲッターまたはセッター、またはリダイレクトしないコンストラクターにすることができます。インスタンス変数externalにすることができ、これは外部ゲッター(および変数がfinalでない場合は)外部セッターと同等です。