現在よく使われているいろいろな言語と同様に、Dartの関数はファーストクラスのオブジェクトである。さらに、Dartにおいては逆にオブジェクトが関数になることができる。
次の2つのコードは全く同じ意味を持つ。
1つ目はオブジェクトの関数を呼び出す方法
class Caller {
String call(String str) {
return str;
}
}
main() {
var caller = new Caller();
print(caller.call("laco")); // laco
}
2つ目はオブジェクトを関数として呼び出す方法
class Caller {
String call(String str) {
return str;
}
}
main() {
var caller = new Caller();
print(caller("laco")); // laco
}
特別なメソッド: call()
Dartではcall()
という名前のメソッドを定義したクラスのオブジェクトは関数になる。引数は自由に設定できるが、オーバーロードはないので一つしかcall()
は定義できない。
このcall()
が定義されたクラスは「関数のように振る舞う」のではなく「関数そのもの」なので、以下のような結果になる
class A{
void call(){
}
}
class B{
void calls(){
}
}
main(){
print(new A() is Function); //true
print(new B() is Function); //false
}
関数としてオブジェクトを渡す
Dartではtypedef
を用いて、独自の関数型を作る。C#をやったことがある方ならDart版delegateだと思っていい。例えばString
を引数に取り、String
を返す関数は以下のように定義する。
/**
* The function of [String] -> [String] converter
*/
typedef String StringConverter(String seed);
定義した関数型は次のように用いることが出来る。
String convert(StringConverter func, String seed) {
return func(seed);
}
このStringConverter
型は、『Stringを引数により、実行するとStringを返す』関数ならなんでも受け入れることが出来るので、先ほどのcall()
と合わせると以下のように関数としてオブジェクトを渡すことができる。継承などは一切必要ない。
class TripleCaller {
String call(String str) {
return "${str}" * 3;
}
}
/**
* The function of [String] -> [String] converter
*/
typedef String StringConverter(String seed);
String convert(StringConverter func, String seed) {
return func(seed);
}
main() {
var laco = new TripleCaller();
print(convert(laco, "laco")); // lacolacolaco
}
もちろん関数型など作らずとも次のように書いてもよい。全ては実行時に解決される。
String convert(Function func, String seed) {
return func(seed);
}