Flutter-Function

ちょっと前にteratailでFlutter(Dart)の関数についての質問があったので、ちょっと興味があったので調べてみた。

関数とは

Dartの関数とは何なのだろうか。これについてはDartの言語ツアーに以下の様に書かれている。

Dart is a true object-oriented language, so even functions are objects and have a type, Function. This means that functions can be assigned to variables or passed as arguments to other functions.

Dartは真のオブジェクト指向言語であるため、関数でさえオブジェクトであり、そのタイプはFunctionです。 これは、関数を変数に割り当てたり、他の関数に引数として渡すことができることを意味します。

A tour of the Dart language
A tour of all the major Dart language features.

例えば、以下の様な関数をdart:mirrorのリフレクションライブラリを使って調べてみたら'Closure: (int) => void from Function 'sample1': static.'と表示された。

void sample1(int value) {
  print(value.toString());
}

Closureであって、Functionの派生じゃないんじゃないのか?という疑問を感じたのだが、以下のオブジェクトを調べてみたら、こちらも'Closure: (int) => void'となっていた。

final void Function(int) test = (int value) => print(value.toString());

多分Function派生のものはClosureと呼ぶようにしているのだと思う。
ただこのことを直接書いているところはなかったのだけど。

ということで、一番初めに書いた関数sample2はvoid Function(int)のオブジェクトだということになる。

C#でいうとdelegate/Action/Funcに似たようなものなのだろうか。

どんな感じに使えるのか

List<int>.forEach()メソッドを例に、どんな形で利用できるか見てみた。
この関数はvoid Function(int)のオブジェクトを引数で受け取ることができる。

以下はいくつかのパターンのFunctionオブジェクトを使ってforEachを呼び出した例。

/// 通常のもの
void sample1(int value) {
  print(value.toString());
}

/// optional position parameterを使用した場合
void sample2(int value, [String? str]) {
  print(value.toString() + (str ?? "hoge"));
}

/// 匿名関数を代入したもの
final void Function(int) test = (int value) => print(value.toString());

/// クラス中のメソッドのcallを使用した場合
class Hoge {
  var _sum = 0;
  void call(int value) {
    _sum += value;
  }

  void printValue() {
    print("$_sum");
  }
}

/// クラス中のメソッドを使った場合
class Hage {
  var _sum = 0;
  void add(int value) {
    _sum += value;
  }

  void printValue() {
    print("$_sum");
  }
}

void main() {
  final list = [1, 2, 3];

  list.forEach(sample1);   // 1
  list.forEach(sample2);   // 2

  final hoge = Hoge();    //3 
  list.forEach(hoge);
  hoge.printValue();

  final hage = Hage();   // 4
  list.forEach(hage.add);
  hage.printValue();

  list.forEach(test);   // 5
  final Function c = sample1;   // 6
  final void Function(int) b = c as void Function(int);
  list.forEach(b);
}
  1. sample1
    普通のvoid Function(int)な関数。
    よく使うのはこういうタイプなのだろう。
  2. sample2
    第2引数に順序指定のオプションパラメータを指定しているパターン。
    void Function(int, [String?])という型になる。
    第2引数は省略可能なので、void Function(int)として利用可能なのだ。
  3. Hoge
    callメソッドを用意し、オブジェクト名(int)として利用可能にしたもの。
  4. Hage
    クラスメソッドにvoid Function(int)を用意し、それを呼び出すようにしたもの。
  5. test
    匿名関数を使用したもの。
  6. sample1をFunctionなcに代入し、それをさらにvoid Function(int)にキャストして利用している。
    これはsample1がFunctionの派生なクラスだということを確認したかったために実施したもの。

3,4はオブジェクトメソッドを呼び出すことができるので、オブジェクト内のメンバー変数などの変更などの処理も行える。
上の例では内部_sumに加算するようなメソッドにしている。

ちなみに()は関数呼び出しの単項後置演算子ということになっている。

コメント

タイトルとURLをコピーしました