はじめに
実務で役に立つ知識ではありません!!
動くけど、書いたら首絞められる系のやつです。
ですが、Java Silver SE8では言語仕様の理解として頻出します。
そのため、実務で殆どの出会うことが無い分「この場合はどうだっけ?」と頻繁に疑心暗鬼に陥りました(笑)
この疑心暗鬼に時間やメモリを割くのも非効率なので動きを簡単に整理してみました。
もし、間違いなどがあれば指摘をいただけると嬉しいです。
単項演算子
単項演算子には、ソースコード内の変数又は値に対して、1を足す++
(インクリメント)や1を引く--
(デクリメント)演算子があります。x++
などの表現は、皆さんもよく見ますよね。
なお、この表現は以下のコードと同義です。
// 普通の代入式
x = x + 1;
// 単項演算子(上記と同義)
x++;
見てのとおり、普通に代入式なので**”どこで評価されても”**対象の変数値は書き換わります。
もし、値が変わっていないとすると単項演算子の評価自体がされていない事を疑いましょう。
単項演算子の前置・後置
単項演算子の記法には、前置と後置の2パターンが存在します。
それぞれコードを書きながら整理しようと思います。
前置パターン
変数よりも前に単項演算子を配置する記法を前置と言います。
左辺オペランド(変数)に値が代入されるのは、右辺オペランドの”単項演算子が評価された後”です。
int x = 1;
int y = ++x; // 単項演算子を前置
// それぞれを表示
System.out.println(y); // 表示:2(前置は、単項演算子が評価された”後"に値が代入される)
System.out.println(x); // 表示:2(2行目の単項演算子により値が書き換わっている)
後置パターン
変数よりも後に単項演算子を配置する記法を後置と言います。
左側オペランド(変数)に値が代入されるのは、右辺オペランドの”単項演算子が評価される前”です。
こいつが単項演算子の(私の)印象を悪くする元凶です(笑)
int x = 1;
int y = x++; // 単項演算子を後置
// それぞれを表示
System.out.println(y); // 表示:1(後置は、単項演算子が評価される"前"に値が代入される)
System.out.println(x); // 表示:2(2行目の単項演算子により値が書き換わっている)
後置・前置を組み合わせると
単項演算子は1つの式文にいくらでも使用できます。
そのため、以下のような式文もコンパイルエラーなく実現可能です。
int x = 1;
// 数値 → 2 + 2 + 3
int y = ++x + x++ + x ;
// それぞれを表示
System.out.println(y); // 表示:7
System.out.println(x); // 表示:3
###【 ふんわりした解説 】
式文の評価順序は、通常左から順番に行われます。
ですが、演算子の優先順位によっては順序が入れ替わる可能性があるため、注意が必要です。
今回出てきた演算子の優先順位は以下の通りです。
優先順 | 式の種類 | 具体例 |
---|---|---|
高 | 単項演算子(前置) | ++x(--x) |
↓ | 単項演算子(後置) | x++(x--) |
低 | 算術演算子 | +(-) |
この順序を念頭に置いて「xの値」に注意しながら順番に式文を評価してみます。
文章では説明しずらいので、ふんわりしたイメージ図にしてみました↓↓
基本はxに値を**代入した後に使う(前置)**のか、**代入前に使う(後置)**のか?
上記に関わらず、xそのものの値は書き換わる。
という点を覚えておけば良いかと思います。
単項演算子の出現シーン
具体的にどんなところで単項演算子が出てくるのか整理してみます。
① 条件式編 (出現度:★★★)
条件式に書いた単項演算子でも変数の値は書き換わります。
もちろん、単項演算子は条件式の評価結果(true/false)に左右されることは有りません。
(=条件式がfalseだと、単項演算子の結果が消えて無くなるとかないです。)
if文とwhile文を例にとって整理してみます。
// 【 if文パターン 】
int x = 1;
if (x++ > 1) { // 結果:false(後置のため、式の評価時点ではxは"1")
System.out.println(x);
} else {
System.out.println(x); // 表示:1(式の評価後、単項演算子によりxに”+1"される)
}
System.out.println(x); // 表示:1(念のため笑)
// 【 while文パターン 】 ※あまり代わり映えしません(笑)
int x = 1;
do {
System.out.println(x); // 表示:1
} while (x++ > 1) // 結果: falseのため繰り返しなし
System.out.println(x); // 表示:2
※前置は、単項演算子を使わない場合と同様の動きなので省略します。
② 引数編(出現度:★★☆)
条件式と同様なのですが「もしかして違う?!」と迷った記憶があるので分けて記載します。
よく出てくるSystem.out.println()
と自分で定義した関数の2パターンを例にします。
少し注意が必要なのは、単項演算子や算術演算子を複数組み合わせた場合です。
// System.out.println()
int x = 1;
int y = 1;
int z = 1;
// 単項演算子を使用して表示
System.out.println(++x + " : 前置サンプル"); // 表示:"2 : 前置サンプル"
System.out.println(y++ + " : 後置サンプル"); // 表示:"1 : 後置サンプル"
System.out.println(z++ + z++ + " : 後置サンプル②"); // 表示:"3 : 後置サンプル②"
// 念のため確認
System.out.println(x); // 表示: ”2"
System.out.println(y); // 表示: ”2"
System.out.println(z); // 表示: "3"
※ 後置サンプル②の結果に違和感を持つかもしれませんが、足し算によりたまたまxと同じ値になっただけです。
// 【自作メソッド】
public class Main {
public static void main(String[] args) throws Exception {
int x = 1;
int y = 1;
Main m = new Main();
// 単項演算子を使用して表示
m.displayNum(++x); // 表示:"2"
m.displayNum(y++); // 表示:"1"
// 念のため確認
System.out.println(x); // 表示:"2"
System.out.println(y); // 表示:"2"
}
private void displayNum(int num) {
// 単項演算子の結果を受け取って表示
System.out.println(num);
}
}
※ returnで単項演算子を使用した場合も、同様の結果となります。
return num++;
→ num
がそのまま戻される
return ++num;
→ num + 1
の結果が返る
③ for文の変化式編 (出現度:★☆☆)
他と同様のため、他とは違う引っ掛けパターンのみ少し説明します。
引っ掛けパターン:
以下が両方満たされているときに限ります。
・「for文の外で定義した変数を使用」
(for文内の初期化式で専用のローカル変数定義が多いかと。)
・「条件式が初回からfalseになった」
(1回も処理されないことはレアケースかな。array.lengthならあり得そうだけど。)
具体的には、以下のようなコードです。
int i = 0;
for(i = 0; i > 0; i++) {
System.out.println(i); // 条件式がtrueになることがないため実行されない
}
System.out.println(i); // 表示:"0" 変化式自体が実行されないため
for文中の処理が1度も呼ばれないため、その後の変化式も評価されることがありません。
よって変数が書き換えることも無いという訳です。
まとめ
ざっくり思いつくものを書き出してみました。
ただ、ここまで書いておいてなんですが、業務でこれらのコードを書くと犯罪行為に近しいかと思います。。。
言語仕様として「こう書いたら、こう動く」という教科書的な知識の習得と割り切りましょう。。
ちなみに、この動きを教科書や問題集だけで追うのは正直キツイです。
クラウド上でコードがかける便利なサービスもあるので、実機確認しながら腹落ちさせていきましょう。(↓)
【コード作成】paiza.IO
参考
・オラクル認定試験教科書 Java プログラマ Silver SE8 (紫本)
・Javaプログラミング ~陥りやすいミス①~
・演算子の優先順位と結合規則