Kotlinを書いている人にとっては馴染みの深い「拡張関数」がDart 2.6で使えるようになりました。
(ちなみに、Dartでは "static extension methods" と呼ぶようです)
https://github.com/dart-lang/language/issues/41
拡張関数を書くには?
拡張関数の定義はトップレベルでのみ可能です。
extension StringExt on String {
int toInt() {
return int.parse(this);
}
}
Dartの拡張関数は、extensionを定義したファイル全体に適用されます。
こんな書き方をする人は居ないとは思いますが、
main() {
print("123".toInt() + "456".toInt());
}
extension StringExtension on String {
int toInt() {
return int.parse(this);
}
}
このようにファイルの末尾に書いたとしても、同一のファイルに書いてあるなら
$ dart main.dart
579
このように、拡張関数が適用されて実行されます。
Dartに限らず拡張関数というもの全般に言えることですが、「便利だから」といって拡張関数に依存した処理を書きまくるのは良い習慣ではありません。拡張関数は用途ごとにファイル別で定義し、使いたいファイルでのみimportして使うのがおそらく現実的な運用でしょう。
dartx パッケージ
Androidアプリ開発では、KTXという拡張関数をつめこんだパッケージがありますね。
Dartだと2019年12月現在、公式(または準公式)の拡張関数パッケージはまだありません。
ただ、非公式で拡張関数をいろいろ詰め込んだ dartx というパッケージがあります。
https://pub.dev/packages/dartx
先にも書いたように、拡張関数に依存した処理を作り込みすぎるリスクがあるので、dartxパッケージを採用しようというケースは多くはないでしょう。しかしながら、拡張関数を書きたいとなったときに、dartxの書き方は大いに参考になります。
例: ?.let
in Dart
たとえば、Kotlinでよく使うイディオムで
selectedItem?.let{ showInImageView(it.imageUrl) }
のように nullableな変数に ?.let
を使うことがありますよね。
「これDartでも使いたいよね?」ってなったときにどう書けばいいでしょう?
Objectに .let
のメソッドを生やせばいいわけですが、クロージャーに渡る引数はObject型だと困りますよね。
dartxのソースコードを見ると、こんな書き方をしている場所があります。
extension NumX<T extends num> on T {
...
つまり、Objectを継承したT型に拡張関数を生やすとよさそう、ということがわかります。
extension ObjectExt<T extends Object> on T {
ReturnType let<ReturnType>(ReturnType operation_for(T self)) {
return operation_for(this);
}
}
class MyValue {
final int value;
MyValue(this.value);
}
main() {
final val1 = MyValue(3);
final val2 = null;
print(val1?.let((it) => it.value)); // => 3
print(val2?.let((it) => it.value)); // => null
}
まとめ
Dart2.6で拡張関数が使えるようになったことで、Kotlinのように可読性の高いメソッドを自由に生やすことが可能になりました。
現時点ではまだandroid-ktxのような拡張関数をつめこんだ公式パッケージはありませんが、今後そのような公式パッケージが出てきたら、Dart/Flutterはより書きやすい言語/フレームワークになっていくのではないでしょうか。