【Flutter】Dartを学習していきなり驚いた拡張関数

Googleが開発しているプログラミング言語「Dart」を最近学習し始めました。基本的な文法や特徴を動かしながら学習するスタイルをとっています。その中で「拡張関数」と呼ばれるものに触れて、便利さと危うさを感じたのでお伝えしたいと思います。

拡張関数(Extension methods)とは

拡張関数はKotlinでは以前から実装されているそうですが、Kotlinはあまり詳しくないのでDartを学習して初めて拡張関数に触れました。この拡張関数ですが、一言で言うと既存の型に関数を追加することができる機能です。しかも、利用する側は拡張された型であるかどうかを気にせず使えてしまいます
詳細は下記の公式サイトを見ると分かりますが、今回は基本部分を説明します。

https://dart.dev/guides/language/extension-methods

他の言語と比べて何がスゴイか

既存のString型に文字列型を数字型に変換する関数を追加したいと考えた場合、String型を継承してparseInt()を追加するようなやり方になるでしょうか。JavaだとStringはfinal宣言されているので、String型を内包したStringExクラスを作成し、parseInt()を定義するやり方になります。


public class StringEx {
  private String str;
  public StringEx(String s){
    str = s;
  }
  public int parseInt() {
    return Integer.parseInt(str);
  }
}

さらには使う側もStringExを意識する必要があります。


public class Main {
  public static void main(String args[]){
    StringEx s = new StringEx("123");
    System.out.println(s.parseInt());
  }
}

これがDartで拡張関数を使うと下記となります。(Runボタンで実行してみてください)

Javaと比較してコード量が劇的に少ないと言うこともありますが、8〜10行目のところが最初に驚いたところです。Stringを宣言しているのに、実質的にStringExが使われているように見えるところに驚きました。
さらには10行目を見ると文字列を定義するとStringExになっているように見えます。
1行目のextensionによりクラスを定義しているわけではなく、2行目の拡張関数をStringに追加しているところがポイントだと思いますが、いざ使うタイミング(9〜10行目)になると、関数が拡張されていることを意識せずとも使えてしまいます。
これにより、簡単かつ柔軟にクラスの機能を拡張できるようになります。今回のサンプルでは便宜上1つのファイルに含めていますが、別ファイルに切り出せば、必要に応じてimportすれば良くなるので、影響範囲も極めて限定的に利用できるようになります。

拡張関数の危ういところ

言語仕様としての危うさではなく、設計者側が注意するべきところですが、簡単に拡張できるが故に影響範囲が広くなりすぎたり、思わぬ副作用を引き起こす可能性もあると思われます。

例えば、Objectクラスを拡張することが考えられます。DartではJavaと同じく、全てのクラスはObjectを継承しています。よって、Objectクラスに拡張関数を10個も20個も追加してしまうと、全クラスを拡張してしまうため、無駄が多く、不具合を誘発しかねません。 きっちり設計していけばそのようなことにならないと思いますが、時間に追われ安直な判断を繰り返すと、いつの間にかアンチパターンに陥っているということは良くあることです。
公式サイトでも記載されていますが、拡張関数は非常にパワフルですが、大きな責任が伴うと。意識して使わないといけませんね。

最後に

この機能は本当にパワフルだと思います。痒いところに手が届くと言う印象で、提供されている既存のクラスやライブラリでは実現できないことを、extensionと書くだけで解決できてしまう印象です。Javaだと別途サブクラスを作ったり、Utilityクラスを作ったりして実現していたところを、ほんの数行で実現できるパワフルさは計り知れません。使い所は気をつけないと、無駄に拡張させたり、似て非なる機能が量産される可能性もあるでしょう。
Dartの学習を始めたばかりですが、いきなり驚かされるのって楽しいですね。それでは、また。

Twitterでフォローしよう

Pickup
おすすめの記事