S-JIS[2008-08-09/2008-08-30] 変更履歴

Java演算子・式の評価

他言語の演算子


Javaの演算子優先順位

グループ 演算子 概要 結合 説明
一次式 ( )
new
.
[]
優先する式 (1 + 2)    
新規生成 new 型[]
new クラス()
配列生成
クラスのインスタンス生成
メンバー obj.value
obj.method()
フィールドアクセス・メソッド呼び出し
配列 array[0] 配列
後置式 ++ -- 後置inc/dec n++
n--
後置インクリメント・デクリメント
単項演算子 ++ --
+ -
~
!
( )
前置inc/dec ++n
--n
前置インクリメント・デクリメント
符号 +n
-n
正符号・負符号
ビット否定 ~n ビット反転
論理否定 !b trueをfalseに、falseをtrueにする
キャスト (クラス)obj  
乗除演算子 * / % 乗算・除算・剰余 m * n
m / n
m % n
 
加減演算子 + - 加算・減算 m + n
m - n
 
文字列連結 string + n
n + string
文字列結合
シフト演算子 << >> >>> ビットシフト n << r
n >> r
n >>> r
シフト演算の注意
関係演算子 < >
<= >=
instanceof
大小比較 m < n
m > n
m <= n
m >= n
 
型比較 obj instanceof クラス instanceof
等価演算子 ==
!=
等価
不等価
m == n
m != n
equals()との違い
ビット論理演算子 & ビットAND m & n ビット単位の論理積 booleanの場合
^ ビットXOR m ^ n ビット単位の排他的論理和
| ビットOR m | n ビット単位の論理和
条件AND演算子 && 条件AND b1 && b2  
条件OR演算子 || 条件OR b1 || b2  
条件演算子 ? : 条件 b ? m : n 三項演算子
代入演算子 =
*= /= %=
+= -=
<<= >>= >>>=
&= ^= |=
代入 m = n
m *= n
複合代入演算子

C言語/C++のカンマ演算子は、Javaには無い。

文字列内に書かれている演算(式)を解釈・計算するライブラリー


用語の説明

演算子はオペレーターoperator)、演算対象(値・項)をオペランドoperand)と言う。

単項演算子は、オペランドが1つしかない演算子。例えば「-n」とか「n++」とか。
二項演算子は、オペランドが2つある演算子。例えば「a + b」とか「obj instanceof クラス」とか。
三項演算子は、オペランドが3つある演算子。Javaでは条件演算子「b ? m : n」しか無い。

演算子を組み合わせたものを「式」と呼び、その計算を行うことを「評価」と呼ぶことがある。

結合(左結合・右結合)は、式の中に同一優先順位の演算子が並んでいるときに どちらを先に計算するかの順序決め。
左結合の例:「a + b + c」…加算演算子「+」は左結合なので、左側から先に計算される。すなわち「a + b」が行われてからcが加算される。
右結合の例:「a = b = c」…代入演算子「=」は右結合なので、右側から先に評価される。すなわち「b = c」が先に実行され、次に「a = b」が実行される。


演算全般の仕様

数値昇格(数値の格上げ:numeric promotion)

算術演算では、型の変換が行われることがある。[/2008-08-30]

単項数値昇格unary numeric promotion)

単項演算子(符号の+-や!~、配列のインデックス等)においては、intより短い整数型(bytecharshort)の演算は、intに変換(ワイドニング変換)されて行われる。

二項数値昇格(binary numeric promotion)

二項演算子で、オペランドのどちらかがintより大きい型(doublefloatlong)のとき、その型に変換されてから計算される。
また、intより小さい型ばかりであれば、intに変換されてから計算される。

例えば「byte b」と「short s」に対し「b + s」は、bがintに拡張され、sがintに拡張されてから加算される。
また、int+doubleならintがdoubleに変換されるし、int+longならintがlongに拡張される。float+longならlongがfloatに変換される。
通常の算術演算では、こうなっている方が便利。

シフト演算(<<・>>・>>>)は二項演算子であって単項演算子ではないが、特殊なので単項数値昇格の対象となっている。
したがって論理右シフト演算「>>>」は注意を要する。
例えば「byte b = -1(0xff)」に対して 右へ1ビットシフトして「0x7f」としたい場合、「b >>> 1」では望んだ結果にならない。
なぜなら、bが単項数値昇格してintに拡張されるので、0xffは0xffffffffになる。(1バイトの0xffと4バイトの0xffffffffは、2の補数表現で見ればどちらも-1を意味する。すわなち同じ値である)
この状態で「>>> 1」を行うので、結果は「0x7fffffff」となる。
ここでbyteにキャストすると一番下の1バイトだけが有効となる(0x7fffffff)ので、結果は「0xff」。0x7fにはならない。
したがって、byteのときは「(b & 0xff) >>> 1」、shortのときは「(s & 0xffff) >>> 1」としなければならない。
(ちなみに「& 0xff」を実施すれば必ず正の数となるので、「>>>」でなく「>>」でも同じ結果になる。ってことは、byteやshortに対する「>>>」なんて不要じゃん…


評価順序

Javaでは、評価順序通りに評価が行われることが保証される(C言語では、実装依存(実装や最適化によって異なる)という事も多かった)

つまり、順序が左→右の演算子では、左の評価が終わってから右の評価を行う。
左の評価が(例外発生とかによって)完全に終了しなかった場合、右の評価は行われない。

	int r = func1() + func2();
		↑もしfunc1()で例外が起きたら、func2()は絶対に実行されない。

理論上は、r2=func2()を先に呼び出して、func1()+r2という計算を行っても結果は同じになる。
(意味的には左右を入れ替えてfunc2()+func1()としたのと同じ)
しかしJavaコンパイラーはそういう事はしない。(途中で例外が起きると後のものは実行されないから、順序が重要となる)

メソッドの引数も、左側から順に評価される。

	int a = 1;
	method(a, a = 2, a);
↓
	method(1, 2, 2)と同じ

しかしまぁ、こういうのに依存したプログラミングはするべきじゃないけどね(苦笑)


ちょっと注意の要る演算子

クラスインスタンス生成式

「new クラス名()」によって、クラスのインスタンスを生成する。

newの普通の使い方(new クラス())
無名内部クラスの作り方(new クラス(){ })
内部クラスのインスタンス化(変数.new クラス())
配列のインスタンス化(new クラス[ ])

C++では引数なしコンストラクターの場合は括弧は不要なので「a = new Foo;」という形で書くことが出来たが、Javaでは「new クラス名」の後ろに必ず括弧が必要。[2003-07-06]


シフト演算子

シフト演算子は、整数型に対してのみ使える(浮動小数floatやdoubleでは使えない)。

Javaでは整数の符号の有無を型では管理しない(常に符号有りとして扱われる)。
(C言語やC++では「int」に「signed int」と「unsigned int」がある)
その違いが現れるのはビット右シフト演算なので、右シフト演算子には符号有り用の「>>」と符号無し用の「>>>」が存在する。[2003-07-06]
「>>」ではシフト前の符号が保存されるが、「>>>」では保存されず、最上位ビットに0が入る。 (2の補数表現においては最上位ビットが符号を意味する)
例えば-2(0xfffffffe)に対して「-2 >> 1」は「-1(0xffffffff)」になるが、「-2 >>> 1」は「0x7fffffff」になる。

ただしbyteやshortに対する「>>>」は注意を要する。→数値の昇格


シフト演算子の右オペランドは正の数しか扱えない。というか、負の数を指定して逆方向のシフトになったりはしない。
なぜなら、右オペランドに対し、左オペランドがintの場合は「& 0x1f」、longの場合は「& 0x3f」してから計算されるため。intは32bit、longは64bitなので、その範囲を超えるシフトは無意味、という判断なのだろうか。(64ビット以上シフトすれば、結果は常に0(負数の符号付き右シフトなら-1)だから)

つまり、「int a」に対する「a << -1」は、「a << (-1 & 0x1f)」なので「a << 31」と同じ。


型比較演算子

instanceofは、オブジェクトが該当クラス(を継承している)かどうかを判断する演算子。該当クラスであれば真(true)。[2007-06-11]

interface I {}
class S {}
class A extends S implements I {}
class B {}

のとき、「Object obj = new A();」とすると、「obj instanceof A」は当然trueだし、「obj instanceof S」「obj instanceof I」もtrue。
Object obj = new B();」であれば、「obj instanceof A」「S」「I」は全てfalse。

対象オブジェクトがnullであれば、instanceofはfalseになる。

リフレクションで型を判断する方法


instanceofは、以下のような使われ方をすることが多い。

	Object obj = 〜;
	if (obj instanceof Type) {	//「obj instanceof Type」がtrueならば
		Type t = (Type)obj;	//objTypeのインスタンスなので、Typeへキャストできる
		〜
	}

※正当な参照型のナローイング変換が可能であることをチェックする目的

insntaceofの実行速度


ビット単位の論理演算子

boolean同士に対するビット論理和・論理積(|,&,ついでに^)の結果は、boolean型になる。[2007-11-06]
booleanとintに対するビット論理和・論理積は計算不能(コンパイルエラー)。


複合代入演算子

「+=」や「*=」等の(単純代入演算子「=」以外の)代入演算子複合代入演算子と呼ぶ。

複合代入演算子は、以下のような変換をした式とほぼ等価。

a += b」≡「a = (Type)(a + b)(Typeは、aの型。つまりキャスト)

「ほぼ等価」というのは、aに当たる部分が配列などの計算を伴う式だった場合は、その計算は一度しか行われないから。でも勿論その方が都合いいでしょう。

これ論理右シフト代入演算子「>>>=」で注意を要する。
例えば「byte b = -1」に対して「b >>>= 1」は、「b = (byte)(b >>> 1)」に展開されたのと同じ。すなわち、数値昇格の影響を受ける。
「b = (byte)((b & 0xff) >>> 1」が意図した動作となる。(つまりbyteやshortに対する「>>>=」って意味無い…)


Java目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま