Dart 캐스케이드 연산자

캐스케이드 문법은 두 가지로 나뉩니다: ..?..

동일한 객체에 속성 할당과 메서드 호출을 여러 번 연속 실행할 수 있어 임시 변수를 생략하고 코드를 더 자연스럽고 간결하게 작성할 수 있습니다.

캐스케이드 문법은 연산자가 아니며 Dart 고유의 문법 설탕(syntax sugar)입니다.

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 문법 설명이므로 문법 원리만 이해하면 됩니다. 아래 예제에서는 순수 내장 클래스 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. 널 안전 캐스케이드 ?..

객체가 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();

// 동등한 널 안전 분할 작성법
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이 아니므로 뒤에는 ?..을 쓸 필요 없습니다.
  3. 중첩 캐스케이드 지원
  4. 반환 타입이 void 인 메서드 뒤에 캐스케이드를 붙일 수 없음
  5. .. 는 연산자가 아닌 Dart 문법 설탕입니다
문법 설탕이란

문법 설탕은 프로그래밍 언어 구문 분석 단계에서 동일한 저수준 로직에 제공하는 간략화 구문입니다. 언어 자체에 새로운 기능을 추가하지 않으며 컴파일러/인터프리터가 중간 코드를 생성하기 전 자동으로 간략 구문을 동등한 표준 기본 코드로 변환합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다