前のページ

言語編

次のページ


Typedef (Typedefs)

typedef関数型エイリアス(function type alias)とも呼ばれる。typedefは型表現の為の名前を宣言する。Dartでは関数は文字列や数字と同じくオブジェクトである。関数を変数や戻りの型として使うときにはtypedefプレフィックスを使った関数型エイリアスを使用する。関数型エイリアスはある関数の型に名前を付し、フィールドや戻り値の型を宣言できるようにする。typedefはある関数型オブジェクトがある変数に代入されたときにその型情報を保持する。

typedefを使っていない次のコードを検討してみる:

class SortedCollection {
  Function compare;
  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
  SortedCollection coll = SortedCollection(sort);
  // compareは関数だということしかわからない
  // その関数の型は?
  assert(coll.compare is Function);
}

SortedCollectionクラスのインスタンス変数であるcompareという関数は、コンストラクタの引数であるint型の関数である。このオブジェクトのcompareはどんな型なのかこのままでは解らない。何故ならcompareに代入した時点で型情報が失われてしまうからである。fの型は(Object, Object) → int (ここで→は戻りの型である)ではあるが、compareの型はFunctionである。明示的な名前を使用し、型情報を維持するようにこのコードを変更すれば、デベロッパとツールの双方がその情報を使える。

typedef Compare = int Function(Object a, Object b);
class SortedCollection {
  Compare compare;
  SortedCollection(this.compare);
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}

注意:Dartでは仕様書上は型エイリアスは型式の名前として宣言できるようにするものであるが、現在は型エイリアスは関数のみに制限されている。将来これは変更させる予定になっている。

typedefは単に別名(エイリアス)であるので、任意の関数の型をチェックする手段にもなる。例えば:

typedef Compare<T> = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
  assert(sort is Compare<int>); // True!
}



より具体的な例

typedefによる関数型エイリアスの動作を以下のコードで示す:

typedef String TempToText(var something);
String fromCelsius(var temp) =>
    'Celsius: $temp degreesC, Fahrenheit: ${temp * 9 / 5 + 32} degreesF';
String fromFahrenheit(var temp) =>
    'Celsius: ${(temp - 32) * 5 / 9} degreesC, Fahrenheit: $temp degreesF';
void printTemp(var someTemp, TempToText whichMethod) {
  print(whichMethod(someTemp));
}
main() {
  bool celsius = true; // unit of the someTemp
  var someTemp = 0; // input temperature
  TempToText whichMethod;
  if (celsius)
    whichMethod = fromCelsius;
  else
    whichMethod = fromFahrenheit;
  printTemp(someTemp, whichMethod);
}
/*
Celsius: 0 degreesC, Fahrenheit: 32 degreesF
*/
  • typedefTempToText(温度を入力して文字列を返す)という名前の関数型は、入力がvar即ちdynamic型で戻り値がStringである関数であると定義している。

  • void printTemp(var someTemp, TempToText whichMethod)というメソッドは、someTempというvar型の温度入力と、whichMethodというTempToText型の関数を入力としている。そのボディ部はsomeTempを引数にしてwhichMethodを読んだ結果をプリントする。

  • fromCelSiusfromFahrenheit2つのメソッドはTempToText型の関数で各々摂氏入力と華氏入力を受け付け、それを摂氏と華氏の温度で文字列に変換する。

  • main()メソッドの中ではcelsiusというブール変数で摂氏入力かどうかを指定する。またsomeTempは摂氏または華氏の入力温度をセットする。

  • 次にcelsiusが真かどうかでどのメソッドを使うかを決め、それをwhichMethodという変数にセットする。

  • Mainメソッドの最後の文であらかじめ定義したprintTemp関数を呼び出している。

このコードでは変数whichMethodアノテーションが2か所使われている。最初はprintTemp関数の引数の中で、もう1か所はmain()関数の中での変数whichMethodの宣言のなかで使われている。この2か所をvarで置き換えてもこのコードは正常に動くが、TempToTextvarからStringへの変換の関数だということが明確に解るので、ツールにとってもプログラム作成者にとっても好ましい記述といえよう。また関数fromCelsiusfromFahrenheitの宣言に型アノテーションをしないとツールはその関数はdynamicではなくてStringであるべきだとエラーを表示する:

error
The function 'fromCelsius' has type '(dynamic) → dynamic' that isn't of expected type 
'(dynamic) → String'. This means its parameter or return type does not match what is 
expected.

これも型エイリアスがコードの高品質化に寄与する例である。





前のページ

次のページ