티스토리 뷰

반응형

연산자

다트는 다음 표에 표시된 연산자를 지원한다. 표에서 Associativity는 결합 법칙을 나타낸다. 위에서 아래로 우선순위가 낮아진다. 대부분 연산자는 클래스 멤버로 구현할 수 있다.

 

연산자를 사용하여 표현식을 만들 수 있다.

a++
a + b
a = b
a == b
c ? a : b
a is T

 

 

 

 

연산자 우선순위

연산자는 우선순위를 가진다. 예를 들어, 곱셈 연산자 *는 덧셈 연산자 +보다 우선순위가 높다.

 

다음 두 코드는 동일한 우선순위로 동작하지만 괄호를 사용해서 가독성을 향상시킬 수 있다. 괄호를 사용하는 것을 습관화하자.

if ((n % i == 0) && (d % i == 0)) ...

if (n % i == 0 && d % i == 0) ...

 

 

 

결합 법칙

위의 표에서 결합 법칙은 어떤 객체의 메서드를 호출하는지를 나타낸다. 예를 들어 + 연산자의 결합 법칙은 LEFT이므로 왼쪽 객체의 + 연산자가 호출된다.

 

aVector + aPoint 코드가 있다면 aVector의 + 연산자를 사용한다.

Vector aVector = Vector(10);
Point aPoint = Point(5);

print(aVector + aPoint);  // aVector.+(aPoint)

 

 

연산 연산자

assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // 결과를 double로 반환
assert(5 ~/ 2 == 2); // 결과를 int로 반환
assert(5 % 2 == 1); // 나머지

assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

 

증가, 감소 연산자

다트는 선위와 전위의 증가, 감소 연산자를 지원한다.

var a, b;

a = 0;
b = ++a; // b에 할당하기 전에 a를 증가
assert(a == b); // 1 == 1

a = 0;
b = a++; // b에 할당하고 a를 증가
assert(a != b); // 1 != 0

a = 0;
b = --a; // b에 할당하기 전에 a를 감소
assert(a == b); // -1 == -1

a = 0;
b = a--; // b에 할당하고 a를 감소
assert(a != b); // -1 != 0

 

 

 

동등 연산자와 관계 연산자

 

두 객체의 값이 동일한지 확인하려면 == 연산자를 사용한다. 두 객체가 동일한지 확인하려면 identical() 함수를 사용한다.

 

 

x == y 연산자의 작동 방식

  1. x, y 둘 중 하나가 null이면 false를 리턴, 만일 둘 다 null이면 true가 된다.
  2. x의 ==() 메서드에 y 인자를 전달하여, x.==(y)처럼 호출된다.
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

 

 

 

 

타입 확인 연산자

타입 확인 연산자는 런타임 중 타입을 확인한다. obj이 T 타입이라면 obj is T는 true가 된다.

 

null을 제외한 모든 객체는 Object?의 서브 클래스이므로 obj is Object?는 항상 true가 된다.

 

특정 타입으로 변환이 가능한 경우에만 as 연산자를 사용하자. 변환이 되지 않으면 에러가 발생한다.

 (employee as Person).firstName = 'Bob';

 

만일 특정 타입으로 변환이 가능한지 확실하지 않으면, 다음같이 타입을 확인해서 사용하자.

 

다트는 타입 프로모션(type promosion)을 지원하므로 if 조건문에서 is로 타입을 확인하면 if 블록 내에서 자동으로 타입 변환이 되어 사용된다.

Object employee = Person();

if (employee is Person) {
  employee.firstName = 'Bob';  // 자동 변환 적용
}

 

 

 

할당 연산자

= 연산자를 사용해 값을 할당할 수 있다.  '할당한다'를 '대입한다'라는 말로도 많이 사용한다.

// a에 value를 할당
a = value;

 

 

대입할 변수가 null 일 경우에만 할당하려면 ??= 연산자를 사용한다.

// b가 null일 경우에만 b에 value를 대입, 그렇지 않으면 b는 그대로 유지
b ??= value;

 

 

 

복합 할당 연산자

복합 할당 연산자는 연산과 할당을 결합한 것이다.

var a = 2; // 할당 연산자 사용
a *= 3; // 복합 할당 연산자 사용, 다음 코드와 동일하다: a = a * 3
assert(a == 6);

 

 

 

논리 연산자

 

논리 연산자를 사용하여 부울 식을 반전하거나 결합할 수 있다. OR 연산자는 두 피연산자 중 true가 1개라도 있으면 true가 된다. AND 연산자는 두 피연산자가 모두 true 이면 true가 된다.

 

다음 예제는 done이 false이고, col이 0이거나 3이면 if 블록이 실행된다.

if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}

 

 

 

비트 연산자와 시프트 연산자

 

비트 연산자와 시프트 연산자를 이용해서 각각의 비트를 조작할 수 있다.

큰 값이나 음수 피연산자를 사용하는 비트 연산의 동작은 플랫폼에 따라 다를 수 있다.

final value = 0x22;
final bitmask = 0x0f;

assert((value & bitmask) == 0x02); // AND
assert((value & ~bitmask) == 0x20); // AND NOT
assert((value | bitmask) == 0x2f); // OR
assert((value ^ bitmask) == 0x2d); // XOR

assert((value << 4) == 0x220); // Shift left
assert((value >> 4) == 0x02); // Shift right

// 피연산자 값이 32비트로 마스킹될 때 변경되기 때문에 웹에서는 다른 결과가 될 수 있다.
assert((-value >> 4) == -0x03);

assert((value >>> 4) == 0x02); // Unsigned shift right
assert((-value >>> 4) > 0); // Unsigned shift right

 

 

 

조건 표현식

다트에는 if-else 문을 간결하게 사용할 수 있는 두 개의 조건 표현식을 제공한다.

 

 

조건 ? 표현식 1 : 표현식 2

조건이 참이면 표현식 1을 평가하고 반환하고, 거짓이면 표현식 2를 평가하고 반환한다.

 

var visibility = isPublic ? 'public' : 'private';

 

 

표현식 1 ?? 표현식 2

표현식 1이 null이 아니면 표현식 1을 반환하고, null이면 표현식 2를 평가하고 반환한다.

String playerName(String? name) => name ?? 'Guest';

 

위의 코드를 아래처럼 작성할 수도 있지만 간결하지 않다.

// ?: 조건 표현식을 사용한 버전
String playerName(String? name) => name != null ? name : 'Guest';

// if-else를 사용한 버전
String playerName(String? name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

 

 

 

캐스케이드 표기법

.. 와 ?.. 를 캐스케이드라고 한다. 캐스케이드를 사용하면 동일한 객체에 대해 일련의 작업을 수행할 수 있다. 캐스케이드로 메서드와 속성에 접근할 수 있다. 캐스케이드를 사용하면 임시 변수를 만들지 않고 유연한 코드를 작성할 수 있게 된다.

 

다음 코드는 Paint 클래스의 객체를 만들고 객체의 속성을 수정하는 코드이다.

var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;


위 코드에서는 같은 객체에 접근하기 위해 계속 변수를 사용한다.

다음은 캐스케이드 표기법을 사용한 예제이다. 캐스케이드는 반환되는 값을 무시하고 계속 객체에 대해서 작동하게 된다. 캐스케이드는 코드량도 줄고 가독성도 증가하게 된다.

var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;

 

캐드케이드가 작동하는 객체가 null이 될 수 있으면 ?.. 를 첫 연산자로 사용해야 한다. ?.. 는 null인 경우 캐스케이드 작업을 시도하지 않는다.

var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
querySelector('#confirm') // Get an object.
  ?..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'))
  ..scrollIntoView();

캐스케이드는 중첩도 가능하다.

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();

 

실제 객체를 반환하는 함수에서 캐스케이드를 구성할 때 주의해야 한다. 다음 예제는 write가 void를 리턴하므로 에러가 발생한다.

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // Error: method 'write' isn't defined for 'void'.

 

 

위의 코드를 다음과 같이 사용해야 한다.

var sb = StringBuffer();
sb
  ..write('foo')
  ..write('bar');

 

 

 

스프레드 연산자

스프레드 연산자는 컬렉션을 풀어서 다른 컬렉션에 삽입한다.

var a = [1, 2, 3];
var b = [0, ...a];  // [0, 1, 2, 3]

 

스프레드 연산자는 사실 연산자가 아니기 때문에 "연산자 우선순위"가 없다. 그래서 가장 낮은 "우선순위"를 가진다. 

따라서 다음 코드는 a와 b를 먼저 +연산을 하고 다시 리스트로 만들게 된다.

var a = [1, 2];
var b = [3];
var c = [...a + b];  // [1, 2, 3]

 

 

 

기타 연산자

기타 연산자는 대부분 다른 예제에서 확인할 수 있다.

반응형
댓글
공지사항