複合代入演算子に対する、ちょっとした勘違い

「x += 3;」は「x = x + 3;」とは違うということを知りました。

きっかけ

あるメソッド(foo)の実行時間を確認しようと、こういうソースを書きました。

    int sum = 0;
    for(String arg : args){
        long start = System.currentTimeMillis();
        foo();
        long end = System.currentTimeMillis();
        sum += end - start;
        (以下略)

このソース、sum を int で宣言していますが、これはコンパイルエラーにならず、ちゃんと実行できます。


でも、「sum += end - start」と「sum = sum + end - start」が等価だとすると、

  • 左手側は int
  • 右手側は long (int と long が混在した場合、Java は long と評価する)

int へ long を入れていることになります。
これはコンパイルエラーのハズ・・・


現に、複合代入演算子を使わずに書くとエラー(型の不一致)になります。
なんで上のソースはエラーにならないんだろう・・・?

複合代入演算子は、演算と代入だけをしているわけではない

Java言語仕様を確認したところ、何を勘違いしていたかがわかりました。

 E1 op= E2 形式の複合代入式では、E1 が一度だけ評価される点を除き、E1 の型を T とした E1 = (T))*1(と等価なものになる。
 例えば、以下のコードは正しいものである:
  short x = 3;
  x += 4.6
 そして、これは以下のコードと等価なものであるため、x の値は 7 となる:
  short x = 3;
  x = (short)(x + 4.6);

Java言語仕様 第3版、15.26.2 複合代入演算子

複合代入演算子は、演算と代入とキャストをしているんですね。


言われてみれば、こういうソースを書いた覚えがあります。

    short x = 0;
    x += 3;    // 「x + 3」の評価は int。でも、short へ代入できる!

よく考えたら、勝手にキャストしないとこういう場合に複合代入演算子を使えなくなってしまいます。
(自分で「x += (short)3」とキャストしても、「short + short」の評価は int 型なので同じ問題が起きます)
そうならないように、言語仕様として代入の前にキャストしているんだと思います。

感想

言語仕様として考えれば、確かにキャストまで考慮しないといけないですが・・・まぁ、ふつうはそんな細かいこと気にしないですよね(笑)。

*1:E1) op (E2