[TIL-009] Dart 실전 문법 파헤치기 - Cascade 연산자

이번에도 알고리즘을 푸는데 처음 보는 연산자가 있었다.

그래서 오늘은 처옴 보는 연산자인 Cascade 연산자, `..`에 대해 파헤쳐본다.

 

Cascade 연산자

객체를 한 번만 호출하고 여러 개의 메서드나 속성을 연속적으로 호출할 수 있게 해주는 연산자.
즉, 객체를 여러 번 참조하지 않고 한 줄로 여러 작업을 수행할 수 있도록 도와주는 문법이다.

`..`는 메서드나 속성을 호출하고 있는 객체를 반환한다.

 

사용법

Cascade 연산자를 사용하지 않는 코드

class Person {
  String name = '';
  int age = 0;

  void setName(String name) {
    this.name = name;
  }

  void setAge(int age) {
    this.age = age;
  }

  void introduce() {
    print('My name is $name and I am $age years old.');
  }
}

void main() {
  var person = Person();
  person.setName('Alice');
  person.setAge(25);
  person.introduce();
}
  • `person`객체를 반복적으로 참조해서 코드가 길어지고 가독성이 떨어진다.

 

Cascade 연산자를 사용한 코드

void main() {
  var person1 = Person()
    ..setName('Alice')
    ..setAge(25)
    ..introduce();
    
  var person2 = Person()
    ..name = 'Alice'
    ..age = 25
    ..introduce();
}
  • `person`을 한 번만 선언하고 여러 개의 메서드를 체이닝 방식으로 호출할 수 있다.
  • `..`에 의해 메서드나 속성을 호출한 객체를 다시 반환한다.

 

메서드의 타입이 다른 경우

class Person {
  void sayHello() {
    print('Call sayHello');
  }
  
  int sayName() {
    print('Call sayName');
    return 1;
  }
  
  String sayAge() {
    print('Call sayAge');
    return '';
  }
}

void main() {
  var person = Person()..sayHello()..sayName();
}
  • `sayHello`, `sayName`, `sayAge`는 반환 타입이 다르지만 `..`연산자로 인해 사용이 가능하다.
  • 다른 포스트에는 사용이 안된다고 나와있던데 지금은 왠지 모르지만 잘 동작한다.

 

심화

아래의 코드가 안되는 이유

  var person = Person().sayHello().sayName();
  • `Person().sayHello()`를 호출하면 반환 값이 없으므로 `void.sayName()`가 되어 호출이 불가능하다
  • 그래서 `sayName()`을 호출하기 위해서는 `person`객체가 있어야 하므로 `..`연산자를 `sayHello()`에 사용한다.
  • 그럼 `Person()..sayHello().sayName()`이 되고 마치 `Person()..sayHello() == var person`으로 생각할 수 있다.
  • 그럼 결국엔 `person.sayName()`이라고 생각할 수 있는데 여전히 에러는 발생한다.
  • 이유는 아직 `var person`에 인스턴스(객체)가 할당되지 않은 상태이기 때문이다. 모든 체이닝이 다 끝이 나야 할당이 된다.
  • 즉,  `Person()..sayHello() == var person`가 성립되지 않으므로 `person.sayName()`이라고 생각했던 것도 성립이 되지 않기 때문에 에러가 발생하고 있는 것이다.
  • 그래서 `sayName()`에서 인스턴스를 반환해야 `var person`에 제대로 할당이 되므로, 반환하기 위해 `..`연산자를 사용한다.
  • 최종적으로 `Person()..sayHello()..sayName();`은 정상 동작하여 `var person`에 인스턴스를 할당한다.

 

주의할 점

생성자에서 바로  사용하는 경우

class Car {
  String model;
  
  Car(this.model);
  
  void show() {
    print('Car model: $model');
  }
}

void main() {
  var car = Car('Tesla')..show()..model = 'BMW'; // ✅ 정상 작동

  Car('Audi')..model = 'Benz'; // ❌ 에러 발생!
}
  • `Car('Audi')..model = 'Benz';` 는 객체를 변수에 저장하지 않고 곧바로 cascade 연산자를 사용하므로 에러가 발생한다.
  • 생성자 (Car()) 호출 결과가 더 이상 사용되지 않으므로 객체가 메모리에서 제거될 수 있다.

 

객체가 null일 경우

Person? person;
person?..setName('Bob')..setAge(30); // person이 null이면 아무것도 실행되지 않음
  • `person`이 `null`이면 에러가 발생하지만 `?..`을 사용하여 실행을 막아 에러를 방지한다.

 

정리

  • `..`연산자는 메서드를 호출한 객체를 반환한다.
  • 메서드의 반환 타입이 없거나 다르더라도 `..`를 사용하면 메서드를 호출한  객체를 반환한다.