캐스케이드 문법은 두 가지로 나뉩니다: .. 와 ?..
동일한 객체에 속성 할당과 메서드 호출을 여러 번 연속 실행할 수 있어 임시 변수를 생략하고 코드를 더 자연스럽고 간결하게 작성할 수 있습니다.
캐스케이드 문법은 연산자가 아니며 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)
요약
..는 모든 연산 시 최상단 원본 객체로 돌아가며 메서드 반환값을 받지 않습니다. JavaScript 등 다른 언어의 체이닝과 유사한 기능입니다.?..는 널 안전 캐스케이드로 객체가 null일 때 오류를 막습니다. 체인 맨 앞에만 사용하며 앞 부분이 실행된다면 객체가 null이 아니므로 뒤에는 ?..을 쓸 필요 없습니다.- 중첩 캐스케이드 지원
- 반환 타입이
void인 메서드 뒤에 캐스케이드를 붙일 수 없음 ..는 연산자가 아닌 Dart 문법 설탕입니다
문법 설탕이란
문법 설탕은 프로그래밍 언어 구문 분석 단계에서 동일한 저수준 로직에 제공하는 간략화 구문입니다. 언어 자체에 새로운 기능을 추가하지 않으며 컴파일러/인터프리터가 중간 코드를 생성하기 전 자동으로 간략 구문을 동등한 표준 기본 코드로 변환합니다.