前のページ

言語編

次のページ


演算子 (Operators)

本章ではDart演算子たちと、それを使った(expressions)について解説している。

Dartでは下表で示された演算子たちが定義されている。これらの演算子たちの多くはオーバライド可能である。詳細は「オーバライド可能な演算子」の項を参照のこと。

記述

演算子

優先度

単項後置(Unary postfix)

expr++ expr-- () [] . ?.

16

単項前置(Unary prefix)

expr !expr ~expr ++expr --expr

15

乗除(Multplicative)

* / % ~/

14

加減(Additive)

+ -

13

シフト(Shift)

<< >>

12

ビットAND(Bitwise AND)

&

11

ビットXOR(Bitwsie XOR)

^

10

ビットOR(Bitwise Or)

|

9

関係及び型テスト(Relational)

>= > <= < as is is!

8

等価(Equality)

== !=

7

論理AND (Logical And)

&&

6

論理OR (Logical Or)

||

5

null判定(if null)

??

4

条件式(conditional)

expr1 ? expr2 : expr3

3

カスケード(cascade)

..

2

代入(assignment)

= *= /= ~/= %= += -= <<= >>= &= ^= |= ??=

1

演算子を使うときは式を作る時である。以下は幾つかの演算子式の例である:

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

注意:Dart2ではビットAND、ビットXOR、及びビットOR演算子が関係及び型テストの上に優先度が上がっており、以前の版のコードを2.x版に対応させる際は注意を要する。

この表は優先順序の高いほうから示されている。例えば、乗除算の%は等価演算子の==より高く、==は論理AND演算子の&&より高い。この優先度は、以下の2行は同じやり方で処理が進む:

// 括弧で囲むと読みやすい
if ((n % i == 0) && (d % i == 0)) ...
// 読みづらいが等価な記述は
if (n % i == 0 && d % i == 0) ...

警告:ふたつの被演算数上で機能する演算子たちにおいて、一番左側の被演算数がその演算のどのバージョンが使われるかを決める。例えば、もし VectorオブジェクトとPointオブジェクトを使っていると、aVector + aPointという式は+Vectorバージョンを使う。



代数演算子 (arithmetic operators)

Dartは下表に示すように通常の代数演算子をサポートしている。

演算子

意味

+

加算

-

減算

-expr

単項マイナス、また否定(negation)としても知られる(式の符号を反転させる)

*

乗算

/

除算

~/

除算、整数の結果を返す

%

整数の除算の剰余

例:

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');

Dartはまた前置と後置(prefix and postfix)の増分と減分(increment and decrement)の演算子をサポートしている。

演算子

意味

++var

var = var + 1 (式の値はvar + 1)

var++

var = var + 1 (式の値はvar)

--var

var = var – 1 (式の値はvar – 1)

var--

var = var – 1 (式の値はvar)

例:

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



対等性と関係演算子 (Equality and relational operators)

下表は対等性と関係演算子の意味を挙げてある。

演算子

意味

==

イコール;以下の説明を読まれたい

!=

イコールでない

>

以上

<

以下

>=

以上か同じ

<=

以下か同じ

2つのオブジェクトxyが同じことなのかどうかをテストするには==演算子を使用する。(2つのオブジェクトがまさしく同じオブジェクトなのか知りたいという稀なケースでは、これの代わりにidentical()関数を使用する。)以下は==演算子がどのように機能するかを示している:

  1. もしxまたはynullなら、の市双方ともnullならtrueを返し、ただ一つがnullならfalseを返す。

  2. メソッド呼び出しx.==(y)の結果を返す。(その通り、Overridable operatorsで解説するように、==と云うような演算子はその最初の被演算数上で呼び出されるメソッドである。==を含む多くの演算子をオーバライドすることも可能である。)

以下は各対等性と関係演算子の使用例である:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);



型テスト演算子(Type test operators)

asis及びis!演算子はランタイム中に型をテストするのに手頃である。

演算子

意味

as

型キャスト(typecast) (またライブラリのプレフィックスを指定するのにもつかわれる)

is

そのオブジェクトが指定した型を持っているとtrue

is!

そのオブジェクトが指定した型を持っているとfalse

obj is Tの結果は、もしobjTで指定されたインターフェイスを実装しているときにtrueとなる。例えば、 obj is Objectは常にtrueである。

あるオブジェクトを特定の型にキャストしたいときはas演算子を使う。一般的に、そのオブジェクトを使っているある式がそのあとに続くオブジェクト上でのisテストの短縮形としてそれを使うべきである。例えば以下のコードを考えてみよう:

if (emp is Person) {
// 型チェック
emp.firstName = 'Bob';
}

as演算子を使ってこのコードを短くすることができる:

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

注意:このコードは対等ではない。もしempnullまたはPersonで無いなら、最初のサンプル(isを使った)では何もしない;2番目のコード(asを使った)では例外がスローされる。



代入演算子 (Assignment operators)

既にこれまで示されていたように=演算子を使って値を代入できる。代入先の変数がnullの時に限り、??=演算子を使う。

// valueaに代入
a = value;
// もしbnullのときにvaluebに代入するとき;でないとbはそのままである
b ??= value;

+=のような複合代入演算子は演算子と代入を組み合わせたものである。

=

=

/=

%=

>>=

^=

+=

*=

~/=

<<=

&=

|=

以下は如何に複合代入演算子が機能するかを示す:


複合代入

等価な式

演算子op:にたいし

a op= b

a = a op b

a += b

a = a + b

以下の例は代入と複合代入演算子を使用している:

var a = 2; // = を使った代入
a *= 3; // 代入と掛け算: a = a * 3
assert(a == 6);



単項後置式に関する注意

Javaでもそうであるが、後置演算結果の代入には注意が必要である。たとえば:

main() {
  int i = 10;
  i = i++;
  print(i); // prints 10
}

10とプリントされる。これは定義により

i = (){var r=i; i=r+1; return r;}();

と等価なので、増分されていないriに代入されるからである。従って増分した結果を印刷したいときはこの行は:

i++;

と記さねばならない。通常後置式はforループ以外には使用しないことが推奨される。



論理演算子 (Logical operators)

論理演算子を使うとブール式を反転させたり組み合わせたりできる。

演算子

意味

!expr

以下の式を反転させる (falsetrueに変える、あるいはその逆)

||

論理OR

&&

論理AND

以下は論理演算子を使用した例である:

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



ビット演算子とシフト演算子 (Bitwise and shift operators)

Dartでは数値の個々のビットを操作できる。通常整数に対しては通常ビットとシフトの演算子は使わない。

演算子

意味

&

AND

|

OR

^

XOR

~expr

単項ビット補数(0Unary bitwise complement (0たちは1になる;1たちは0になる)

<<

左シフト

>>

右シフト

以下はビットとシフトの演算子の使用例である:

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



条件式 (Conditional expressions)

Dartではそうしないとif-else文を使うことになる式をコンサイスに評価できる2つの演算子がある:

condition ? expr1 : expr2

conditiontrueならexpr1を計算する(そしてその値を返す);そうでなければ、expr2を計算して返す。

expr1 ?? expr2

もしexpr1が非nullのときはその値を返す;そうでなければ、expr2を計算して返す。

ブール式に基づいて値を代入したいときは? :の使用を検討されたい。

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

ブール式のnullのテストには??の使用を検討されたい。

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

上の例は少なくとも2つのやり方で書け得るが、簡潔なものではない:

// ?:演算子を使った少し長い記述
String playerName(String name) => name != null ? name : 'Guest';
// if-else文を使った非常に長い記述
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}



カスード (..)記述 (Cascade notation)

カスケード (..)SmallTalkで最初に導入されたもので、同じオブジェクト上で操作のシーケンスを行得るようになる。関数呼び出しに加えて、同じオブジェクト上のフィールドにもアクセスできる。これにより一時変数生成のステップが節約され、より簡潔なコードが書ける。

以下のコードを考えてみよう:

querySelector('#confirm') // object取得
  ..text = 'Confirm' // そのメンバを使用
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

最初のメソッド呼び出しquerySelector()selectorオブジェクトを返す。これに続くカスケード表記はこのselectorオブジェクト上で動作し、それが返されるかもしれないどの後の値も無視する。

前のサンプルは以下と等価である:

var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

カスケードをネストすることもできる。例えば:

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

実際のオブジェクトを返すある関数上でカスケードを組み立てるのは注意しなければならない。例えば、以下のコードは動かない:

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

もう一つの例を示す:

main() {
  Map mimeMap = const {
    'rtf': 'application/rtf',
    '3g2': 'video/3gpp2',
    'exe': 'application/octet-stream',
    'abs': 'audio/x-mpeg',
  };
  List keys = mimeMap.keys.toList()..sort();
  for (String key in keys) {
    print('extension: $key, mime type:${mimeMap[key]}');
  };
}
/*
extension: 3g2, mime type:video/3gpp2
extension: abs, mime type:audio/x-mpeg
extension: exe, mime type:application/octet-stream
extension: rtf, mime type:application/rtf
 */

これはあるマップの内容をキーをアルファベット順にソーティングしてプリントするものだが、ここで

List keys = mimeMap.keys.toList()..sort();

List keys = mimeMap.keys.toList().sort();

と書いてはいけない。sortメソッドはkeysに対するメソッド、即ちレシーバはkeysだからである。むしろ

List keys = mimeMap.keys.toList();
keys.sort();

と書いたほうが良い。

注意:厳格に言えば、カスケードのためのこの「ダブル・ドット」表記は演算子ではない。これは単にDartの文法の一部である。



他の演算子 (Other operators)

これまでの例で残った演算子たちの殆どを見てきている。


演算子

名前

意味

()

関数アプリケーション

関数呼び出しを表わす

[]

リスト・アクセス

そのリストの指定したインデックスにある値を参照

.

メンバ・アクセス

ある式のプロパティを参照;例: foo.barは式fooからプロパティbarを選択する

?.

条件付きメンバ・アクセス

同様、しかし一番左の被演算子はnullになり得る;例:foo?.barfoonullでない限り式fooからプロパティbarを選択する(nullの場合はfoo?.barの値はnullである)

.?、及び..演算子に関する更なる情報はClassの章にある。





前のページ

次のページ