余白

https://blog.lacolaco.net/ に移転しました

Dartにおけるジェネリックの意義

Google+で面白い議論がなされていたので簡単にまとめてみる。

元の投稿はDartジェネリックC#と違うことに疑問を投げている

  1. なぜジェネリックメソッドに使えないのか
  2. ジェネリックごとにオーバーロードできないのか
  3. ジェネリックのクラスをリフレクションでインスタンス生成できないのか

投稿者は具体的には以下の様なコードを実現したいらしい

abstract class MyMessage {}

class AMessage extends MyMessage{}

class BMessage extends MyMessage{}

abstract class IHandler<T>{
  void handle<T>(T item);
}

class Handler implements IHandler<MyMessage> {

  void handle<AMessage>(AMessage msg){}

  void handle<BMessage>(BMessage msg){}
}

そもそもDartは動的型付け言語である

投稿者はC#と比較してDartジェネリックの不便さを嘆いているが、忘れてはいけないのは「Dartは動的型付け言語である」ということである。Dartで使われているのは厳密には型ではなくType Annotationという注釈でしかなく、基本的には静的解析のパフォーマンスを上げるために用意されている、エディターのための機能である。

その点で、3のリフレクションを用いてジェネリックタイプのインスタンス生成ができないというのは当然である。なぜなら実行時にはユーザーが定義した型など存在していないからである。そのためにDartの基底クラスObjectにはnoSuchMethodが定義され、オーバーライドが可能になっている。

1と2についてはそもそもDartメソッドオーバーロードができない以上不可能である。Dartはすべてのジェネリックについてdynamic型を受け付けるので衝突が避けられないのだ。C#Java等では、List<T>Listは別の型であるが、Dartでは同じである。Listは暗黙的にList<dynamic>となるし、class List<T>new List()を許容する。

解決策

これらを踏まえた上で、最初に挙げたコードを実現しようとすると次のようなコードが考えられる。既定クラスで引数を受けておいて、実装側で引数の型によって処理を分けることができる。

abstract class MyMessage {}

class A extends MyMessage{}

class B extends MyMessage{}

abstract class IHandler<T>{
  void handle(T message);
}

class MyHandler implements IHandler<MyMessage> {
  handle(MyMessage msg){
    if(msg is A){
      ...
    }else if(msg is B){ 
      ...
    }
  }
}

もしくはIHandler側で型ごとに予めメソッドを定義しておくのも選択肢である

abstract class MyMessage {}

class A extends MyMessage{}

class B extends MyMessage{}

abstract class IHandler{
  void handleA(AMessage message);
  void handleB(BMessage message);
}

class MyHandler implements IHandler {
  handleA(AMessage msg){}
  handleB(BMessage msg){}
}

所感

自分ももともとC#書いてた身なので投稿者の気持ちはよく分かるが、Dartが実行時には動的型付けであるということを意識してコードを書くようになってからは、むしろこのような「オーバーロード禁止」や「メソッドジェネリック禁止」は合理的だと思えている。型はランタイムのためではなくコードを書くときだけに利用されるDartの仕組みは、「厳密に書き、柔軟に動く」という理想の言語を実現することができるのではなかろうか。

[追記] Twitterで「メソッドジェネリックサポートはAcceptedされてる」と指摘を受けたので調べた。

Issue 254 - dart - Support declaration of generic type for methods - Dart - Structured Web Programming - Google Project Hosting

確かに受理されているが、1年以上放置されているので後回しにされているようだ。あまり期待はできないが一応可能性はあるということを覚えておく。