Dart カスケード記法

カスケード記法には2種類存在します:..?..

同一オブジェクトに対し複数回プロパティ代入・メソッド呼び出しを連続で実行でき、一時変数を省略しコードをスムーズに記述できます。

カスケード記法は演算子ではなく、Dart独自のシンタックスシュガーです。

1. 通常カスケード ..

オブジェクトが絶対にnullではない場合に使用

すべての .. 操作は先頭の元のオブジェクトに作用し、各ステップのメソッド戻り値は無視されます。

公式サンプル

import 'dart:ui';

void main() {
  // カスケード記法
  var paint = Paint()
    ..color = Colors.black
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5.0;

  // 同等の分割記法(カスケード不使用、変数名を繰り返す必要あり)
  var paint2 = Paint();
  paint2.color = Colors.black;
  paint2.strokeCap = StrokeCap.round;
  paint2.strokeWidth = 5.0;
}Code language: JavaScript (javascript)

このサンプルではFlutter専用ライブラリdart:uiをインポートしており、Flutter環境以外では利用できません。純粋なDart文法解説のため、文法のみ理解すれば大丈夫です。次のサンプルでは純Dart標準クラスStringBufferを使用し説明します。

文字列操作用クラス:StringBuffer

void main() {
  // カスケード記法
  var sb = StringBuffer()
    ..write("Hello ")
    ..write("FoxDevelop.com ")
    ..writeln("Cascade");

  print(sb.toString());

  // 同等、カスケード未使用
  var sb2 = StringBuffer();
  sb2.write("Hello ");
  sb2.write("FoxDevelop.com ");
  sb2.writeln("Cascade");
  print(sb2.toString());
}Code language: PHP (php)

実行結果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Hello FoxDevelop.com Cascade

Hello FoxDevelop.com CascadeCode language: CSS (css)

上記サンプルのpaint、sb、sb2はいずれもnullにならないことが確定している場合にカスケードを使用すると、オブジェクト名を繰り返さず一括代入可能です。一方、オブジェクトがnullの場合は直接エラーが発生します。

void main() {

  var sb = null
    ..write("Hello ")
    ..write("FoxDevelop.com ")
    ..writeln("Cascade");

  print(sb.toString());
}Code language: JavaScript (javascript)

この問題に対応するため、2種類目の記法が用意されています。

2. Null安全カスケード ?..

オブジェクトが null の場合、カスケードチェーン全体が実行されずNull参照エラーを回避できます。チェーンの先頭のみ ?.. を使用し、後続は通常の .. を記述します。

void main() {

  var sb = null
    ?..write("Hello ")
    ..write("FoxDevelop.com ")
    ..writeln("Cascade");

  print(sb.toString());
}Code language: Dart (dart)

上記の ?..write(“Hello “) 先頭に疑問符が付いている点に注目してください。

再度コンパイルすると正常に動作します

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
nullCode language: CSS (css)

エラーは発生しません

以下は公式サンプルです。文法形式のみ把握すれば問題ありません。

// 先頭に?..、後続は一律..を使用
document.querySelector('#confirm')
  ?..textContent = 'Confirm'
  ..classList.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'))
  ..scrollIntoView();

// 同等のNull安全分割記法
final button = document.querySelector('#confirm');
button?.textContent = 'Confirm';
button?.classList.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();Code language: JavaScript (javascript)

ネストカスケード

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      // 内部のPhoneNumberBuilderに個別カスケードを適用
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();Code language: PHP (php)

phoneはPhoneNumberBuilder()で生成したオブジェクトに値を代入した変数です。

注意事項

カスケードはオブジェクトインスタンスに対してのみ利用可能で、void戻り値に適用するとコンパイルエラーが発生します。

var sb = StringBuffer();
sb.write('foo') // writeはvoidを返す
  ..write('bar'); // エラー:void型にwriteメソッドは存在しないCode language: Dart (dart)

こちらは先にStringBufferオブジェクトsbを作成しています

しかしセミコロンで文を終了した後、sbからwriteメソッドを呼び出しており、writeの戻り値はvoidのため、その後に..writeを連ねることはできません。writeがオブジェクトインスタンスを返す場合のみ連結可能です。

正しい記述方法

var sb = StringBuffer()
  ..write('foo')
  ..write('bar');Code language: Dart (dart)

まとめ

  1. .. は各操作で常に最上位の元オブジェクトに戻り、メソッドの戻り値を受け取りません。他言語(JavaScriptなど)のチェーニングと同等の動作を実現します。
  2. ?.. はNull安全カスケードで、オブジェクトがnullの際のエラーを防止します。チェーン先頭のみ使用し、先頭が実行できる時点でオブジェクトはnullではないため後続に?..は不要です。
  3. ネストしたカスケードに対応
  4. 戻り値が void のメソッド後にカスケードを連結できない
  5. .. は演算子ではなくDartのシンタックスシュガーです
シンタックスシュガーとは

シンタックスシュガーとは、プログラミング言語の構文解析段階で同一の底层ロジックに対し提供される簡略記法のことです。言語の機能拡張は行わず、コンパイラ/インタプリタが中間コード生成前に自動的に簡略記法を標準な基礎コードに展開します。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です