Dartの`@`から始まるMetadataを理解する


## 前振り

[Redstone.dart](https://github.com/luizmineo/redstone.dart) の[installationのソース](https://github.com/luizmineo/redstone.dart/wiki/Installation)を見ると、`@app.Route("/")` というコードが出てきます。

```dart
import 'package:redstone/server.dart' as app;

@app.Route("/")
helloWorld() => "Hello, World!";

main() {
  app.setupConsoleLog();
  app.start();
}
```

このコードを起動して localhost:8080 をブラウザで開くとHelloWorldになりますし、

![localhost_8080.png](https://qiita-image-store.s3.amazonaws.com/0/3865/0ceff551-b216-5d15-59c0-7446629ee892.png "localhost_8080.png")

``@app.Route("/")``の行だけをコメントアウトするとNotFoundになります。

![Redstone_Server_-_NOT_FOUND.png](https://qiita-image-store.s3.amazonaws.com/0/3865/53e9e0de-6c66-5921-28c0-e3a1b46e7812.png "Redstone_Server_-_NOT_FOUND.png")

実際に[RedstoneのRouteのソース](https://github.com/luizmineo/redstone.dart/blob/master/lib/src/metadata.dart#L3-L48)を読んでも、特に特殊なメタメタした処理などない、ただのconstコンストラクタを持った普通のクラスになっています。

以下の引用部分は、[Redstone.RouteクラスのDoc](https://github.com/luizmineo/redstone.dart/blob/master/lib/src/metadata.dart#L4-L12)の翻訳です。（ちなみに[Redstone.dartはMITライセンス](https://github.com/luizmineo/redstone.dart/blob/master/LICENSE)で提供されています）

> ターゲットを定義するためのアノテーション
> [urlTemplate] はターゲットのURLで、`:`から始まるプレフィックすをつける事で引数を設定する事もできます。
> [methods] はこのターゲットにおいて許可されるHTTPメソッドで、デフォルトではGETが指定されています。
> [responseType] は、レスポンスのContent-Typeです。もし指定が無ければメソッドの返り値を元にフレームワークが決定します。
> [statusCode] は、レスポンスのステータスのデフォルト値で、もし氏知恵がない場合は200が使われます。 
> [allowMultipartRequest] がtrueの場合は、Routeクラスは(ファイルアップロードなどの）multipartリクエストを受け付けられるようになります。
> 使用例:
> @app.Route('/user/:username', methods: const[app.GET, app.POST])
>		helloUser(String username) => "Hello, $username";

このように、たかがアノテーションごときでかなりいろんな設定を持たせる事ができるようで、推測するに定数的な動作をさせるにあたってかなりパワフルに、かつ、コードを適切に分割できる強いツール（構文）なのかと思われますが、いまいち理解できてないので[公式ドキュメント](https://www.dartlang.org/docs/dart-up-and-running/ch02.html#metadata)を読んでみます。

-----

なお、公式ドキュメントに出てくる「Type Annotation」とは型注釈の事で、`var hoge = something()` ではなく `String hoge = something()` のように型指定する事を指すようです。

`@hoge` に関しては、`metadata`とよばれます。

ここから下が翻訳です。

-----

## メタデータ


> 原文: [https://www.dartlang.org/docs/dart-up-and-running/ch02.html#metadata](https://www.dartlang.org/docs/dart-up-and-running/ch02.html#metadata) (2014/12/14 時点のもの)
> なお、公式ドキュメントは[Creative Commons 3.0とBSDライセンスで提供](https://github.com/dart-lang/www.dartlang.org/blob/master/LICENSE)されており、翻訳に際して著者に連絡を取ったりはしていませんが、問題があればご指摘ください。



コードに付加情報を与えるには、メタデータを使います。メタデータアノテーションは`@`から始まり、コンパイル時定数（例えばdeprecated)を参照するか、constantコンストラクタを呼び出します。

全てのDartコードで、次の3つのアノテーションを利用する事ができます。`@deprecated`、`@override`、`@proxy`の3つです。`@override`と`@proxy`の使用例は [Extending a class](https://www.dartlang.org/docs/dart-up-and-running/ch02.html#extending-a-class) を参照してください。ここでは、`@deprecated`アノテーションの使い方を見てみましょう。

```dart
class Television {
  /// _Deprecated: 代わりに [turnOn] を使ってください_
  @deprecated      // Metadata; activate()を使おうとすると Dart Editorが警告を出します
  void activate() {
    turnOn();
  }

  /// テレビの電源をオンにします
  void turnOn() {
    print('on!');
  }
}
```

好きなメタデータアノテーションを定義する事もできます。ここでは、2つの引数を受け取る `@todo` アノテーションを定義してみましょう。

```dart
library todo;

class todo {
  final String who;
  final String what;

  const todo(this.who, this.what);
}
```

そして、次にこの `@todo` アノテーションを利用してみましょう。

```dart
import 'todo.dart';

@todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}
```

メタデータは、`library`、`class`、`typedef`、型パラメータ、コンストラクタ、`factory`、関数、フィールド、パラメータ、変数などの宣言や、インポートやエクスポート命令より前に現れます。リフレクションを利用する事で、実行時にメタデータを取得する事ができます。

-------

翻訳ここまで。

## つまりReflectionしろってこと？

なるほどわからん。が、基本的にはIDEが警告を出してくれる程度のものでしかなくて（これは想像どおり）、これを有意に利用しようとするにはReflectionが必要との事なのだろうか？

という事は、Redstone.dart でもどこかでリフレクションしているはずなのでコードを見てみました。

実際は複雑な処理がいろいろ行われてるんですが、簡単にまとめると以下のような事をやっているようです。

```dart
import 'dart:mirrors';

class Foo {
  final String _something;
  const Foo(this._something);
}

@Foo("bar")
helloWorld() => "Hello, World!";

main() {
  currentMirrorSystem()
    .isolate
    .rootLibrary
    .declarations
    .forEach((Symbol symbol, DeclarationMirror declaration) {
      print("${symbol} のメタデータは ${declaration.metadata} です");
    });
}
```

これの出力は

```txt
Symbol("Foo") のメタデータは [] です
Symbol("main") のメタデータは [] です
Symbol("helloWorld") のメタデータは [InstanceMirror on Instance of 'Foo'] です
```

のようになり、Symbol `helloWorld` のメタデータとして `[InstanceMirror on Instance of 'Foo']`、つまりFooのconstインスタンスを取得できる事がわかります。

実際の処理は[Redstone.dartのsetup_impl.dartの_scanHandlers()関数](https://github.com/luizmineo/redstone.dart/blob/master/lib/src/setup_impl.dart#L292-L438)あたりを追いかけると知る事ができます。

## 感想

基本的には `@hoge` アノテーションは、メタデータ、つまり付加情報でしかなく、IDE等においての開発の手助けになる、が主な利用目的である事がわかります。

しかしながらReflectionを利用する事で、メタデータを取得する事ができ、そのメタデータにはコンパイル時定数かconstインスタンスが自由に設定する事ができるので、今回のRedstone.dartのルーティング設定を行うというのは非常にGoodなアイディアである事が分かります。

Reflection周りの操作は基本的には重く、可能な限り避けるべきだと思っていますが、アプリケーション起動時に1度だけReflectionを行って、以後その設定を使いまわすというのであれば、Reflectionのコストを気にする必要もありませんし、コード上もすっきりして見やすく、Reflection周りの処理をライブラリ側（今回でいえばRedstone.dart側）が隠蔽してくれるという前提で考えると、使い道も広がってくるなーと思いました。

とはいえ、`@deprecated`、`@override`、`@proxy`についてはDartが元々定義しているアノテーションなので、よくよく出てきますので、意味や使いドコロについては知っておくと良いと思います。