見出し画像

プリミティブ型を扱うために用意されている各種の演算子について

JavaSilverの資格取得のため勉強中ですが、アウトプットについても勉強中です。
とりあえず今回はJavaでプリミティブ型を扱う演算子をできるだけ紹介していきたいと思います。

また、すべてについて解説をしていくと量が多くなってしまうので、私が面白い、役に立つと思った部分をピックアップしてお伝えしていく方針で進めていきたいです。

毎度のことながら参考書はこちら↓

ただ、今回は公式ドキュメント等もかなり参考にしており、書籍の内容は少なめになっていますのでご注意ください。
では行ってみましょう。

Javaにおける演算子について


プリミティブ型はデータ値です。オブジェクト(=インスタンス)ではないため、メソッドがないので値の操作ができません。(ラッパークラスについてはいつか説明したいですが、今はおいておきます)
というわけで、Javaでは、プリミティブ型データの操作を行うための演算子が用意されています。
以下できるだけ細かく分類してみました。
◆算術演算子
 ・符号演算子
  ・単項プラス符号演算子(+var)
   ・単項マイナス符号演算子(-var)

 ・加算演算子(+)

 ・減算演算子(-)

 ・乗算演算子(*)

 ・徐算演算子(/)

 ・剰余演算子(%)

 ・インクリメント演算子
  ・前置インクリメント演算子(++var)
  ・後置インクリメント演算子(var++)

 ・デクリメント演算子
  ・前置デクリメント演算子(++var)
  ・後置デクリメント演算子(var++)

◆比較演算子(関係演算子)
 ・等価演算子(==)

 ・不等価演算子
  ・"等しくない"演算子(!=)
  ・"より大きい"演算子(>)
  ・"以上"演算子(>=)
  ・"未満"演算子(<)
  ・"以下"演算子(<=)

◆論理演算子
 ・論理和(OR)演算子
  ・非短絡評価の論理演算子としては(|)
  ・短絡評価の論理演算子としては(||)

 ・論理積(AND)演算子
  ・非短絡評価の論理演算子としては(&)
  ・短絡評価の論理演算子としては(&&)

 ・排他的論理和(XOR)演算子(^)

 ・論理否定(NOT)演算子(!)

◆代入演算子
 ・単純代入演算子
  ・代入演算子(=)

 ・複合代入演算子
  ・加算代入演算子(+=)
  ・減算代入演算子(-=)
  ・乗算代入演算子(=)
  ・除算代入演算子(/=)
  ・剰余算代入演算子(%=)

◆ビット演算子 ※この内の3つのビット演算子はシフト演算子とも呼ばれる
 ・bit毎のOR演算子(|)

 ・bit毎のAND演算子(&)

 ・bit毎のXOR演算子(^)

 ・bit毎のNOT演算子(~)※「ビット反転演算子」とも呼ばれる

 ・左シフト演算子(<<)

 ・符号を維持する右シフト演算子(>>)

 ・0 埋め右シフト演算子(>>>)

◆その他の演算子
 ・条件演算子(?:) ※「3項間演算子」の名前で呼ばれることが殆ど

 ・instanceof演算子 (instanceof) ※「型比較演算子」とも呼ばれる

 ・new演算子(new)

 ・ドット演算子(.)

 ・cast演算子(())

演算子の優先順位

演算子を優先順位の高い順番に示します。
点線で囲まれているグループは同列で、左結合や右結合というのは右と左のどちらから演算していくかの順番を表します。
これについてもたくさんの記事で触れられているのでぜひ調べてみていただきたいです。
----------------------------------------------------------------------------
()、new、.、[] ※結合規則:左結合
※()は優先順位を上げる為に使われるセパレーター
※newはインスタンス生成を行うnew演算子
※.はfieldやmethodを参照するのに使われるドット演算子
※[]は配列のindexを指定するのに使われるセパレーター
----------------------------------------------------------------------------
単項演算子(+、-、++、--、~、!、cast) ※結合規則
----------------------------------------------------------------------------
算術演算子(*、/、%) ※結合規則:左結合
----------------------------------------------------------------------------
算術演算子(+、-) ※結合規則:左結合
----------------------------------------------------------------------------
シフト演算子(<<、>>、>>>) ※結合規則:左結合
----------------------------------------------------------------------------
比較演算子(<、>、<=、>=、instanceof) ※結合規則:左結合
----------------------------------------------------------------------------
比較演算子(==、!=) ※結合規則:左結合
----------------------------------------------------------------------------
ビット演算子(&) ※結合規則:左結合
----------------------------------------------------------------------------
ビット演算子(^) ※結合規則:左結合
----------------------------------------------------------------------------
ビット演算子(|) ※結合規則:左結合
----------------------------------------------------------------------------
論理演算子(&&) ※結合規則:左結合
----------------------------------------------------------------------------
論理演算子(||) ※結合規則:左結合
----------------------------------------------------------------------------
条件演算子(?:) ※結合規則:右結合
----------------------------------------------------------------------------
代入演算子(=、*=、/=、%=、+=、-=、<<=、>>=、>>>=、
&=、^=、|=) ※結合規則:右結合

オペレーターとオペランド

「1 + 2」という式を考えたときに、+演算子をオペレータ(「演算子」を意味する英単語)、処理対象の1と2をオペランド、と呼びます。

ちなみに+演算子は、オペレータの型によって振る舞いが変わり、オペレータの両方が数値の場合はオペレータに対して「加算」を実行し、オペレータのどちらか1つでも文字列の場合はオペレータに対して「文字列結合」を実行する(内部的には、+演算子はメソッドであり、引数の型に応じてオーバーロードされている状態のようです)

オペランドを1つ取る演算子(Operator)のことを単項演算子と呼び、2つ取る演算子のことを二項演算子と呼び、3つ取る演算子のことを三項演算子と呼びます。

一覧表からいくつか紹介すると、インクリメント演算子は単項演算子、加算演算子は二項演算子、条件演算子は三項演算子です。

式と文

文とは「それ単独で完結する言語要素」
Javaでは文には末尾にsemi-colon(;)を付与しなければならない。
例としては代入文、if文、for文、while文、などがあります。

式とは「それ単独では基本的に完結せず、文または式の一部として使用される言語要素」
式は「値を返す」という特徴がある(文は値を返さない)
例としては条件式(比較式)や四則演算式などに代表される「値、変数、演算子、関数の組み合わせ」
具体例を挙げると「2 + 3」とか「intVal == 3」とか「strVal != null && strVal.isEmpty()」など

式の「評価」とその「評価結果」

プログラミングでは、「式を実行する」ことを「式を評価する」という表現をすることが多い。「式の実行結果」のことは「式の評価結果」と呼ぶ。

例えば「1 + 2」という式を考えたときには、「1 + 2を評価した結果は3である」などと言う。

評価の順番は、演算子の結合規則と優先順位で決まる。


インクリメントについて

インクリメント演算子については、自分を含めて初学者は混乱しやすい部分であると思うので前置と後置の違い等について説明をしていきたいと思います。

まずは前置インクリメントについて

int a = 10;
int b = ++a;
System.out.println(a); //11を表示
System.out.println(b); //11を表示

int型の変数bには a = a + 1; したものを代入しています。
一方で後置インクリメントは次のようになります。

int a = 10;
int b = a++;
System.out.println(a); //11を表示
System.out.println(b); //10を表示

今までインクリメントを使う機会がなかった方は少し混乱するかもしれませんが、ここまでの状況をまとめると。

①前置インクリメントした場合、インクリメントされたaも代入したbも加算されている。
②後置インクリメントの場合は
 (1)インクリメントされたaは加算されているが
 (2)bにはインクリメントされる前の一列目、 a = 10; の値が代入されて   いる。

つまり、後置インクリメントは代入などの演算子と組み合わせて使う場合、インクリメントする前の値を処理に使い、その処理が終わった後にインクリメントされていることになります。

これを利用すると次のような表記ができます。
少し考えてみて、出力した場合にいくつが表示されるか考えてみましょう。

int a = 10;
int b = a++ + ++a;



流れとしては
①まず、int型の変数aに10を代入
②int型の変数bに代入するのは…
 (1)a++ → インクリメントする前のaなので値は10 さらにint a = a + 1; が実行される。この時点でint a = 11; となる。

 (2)++a → (1)の終了時点でint a = 11;であるが、このaにインクリメントするので、int a = 12; となる

 (3) つまり、(1)のaと(2)のaを足すことになるので、10+12=22となり、変数bには22が代入されます。

 ということで、最終的な値はa = 12、b = 23
となります。

論理演算子 ~短絡評価と非短絡評価の違い~

短絡評価→(||)(&&)
非短絡評価→(|)(|)

・短絡評価は、左辺だけで結果が確定すれば右辺は省略する

・もし、右辺のインクリメント演算子を付けており、評価後に必ず値を増やしたい場合等は非短絡評価演算子を使うことで、右辺までしっかり式を評価することができる。

なかなか非短絡評価を使う場面は少ない(私は使用経験がありません)ので基本的には短絡評価で問題ないと思いますが、もちろんケースバイケースなので頭の片隅に入れておいてここぞという場面では使えるようにしておきたいですね。


おまけ プリミティブ型に対応する参照型

プリミティブ型同士の演算は各種の演算子を使って行えるが、「要素として参照型しか扱えない」List(Listの実装がObjectの配列を保持するようになっているから。)などのCollectionを使いたい場合に、プリミティブ型であるがゆえにこれらを使えないのは不便である。

それぞれのプリミティブ型に対応する参照型が用意されている。
※下記の表には対応するプリミティブ型がない数値計算用の参照型も合わせて掲載した(表の一番下の2つ)

primitive型  |    対応する参照型
boolean      |     Boolean
byte            |    Byte
short           |    Short
int               |    Integer
long            |    Long
float            |    Float
double        |    Double
なし            |    BigInteger
なし            |    BigDecimal
char             |    Character

変換方法

旧式
「プリミティブ型」を「対応する参照型」に変換するには、その参照型のコンストラクタの引数にプリミティブ型の値を渡してあげれば良い。
例)boolean値trueをBoolean値に変換するには、new Boolean(true)とする。

例)int値100をInteger値に変換するには、new Integer(100)とする。

例)boolean b1 = true; Boolean b2 = new Boolean(b1);

例)int i1 = 1; Integer i2 = new Integer(i1);

ただし、これらの方法はJava1.9からは非推奨となっています。

このように、プリミティブ型を包む感じで参照型のオブジェクトを作成するのだが、「何かを包んで使うようなクラス」のことを『ラッパークラス』と呼ぶ。

話が少しそれてしまいましたが、現在推奨されている変換方法は

『valueOf』というstaticメソッドを使う変換方法です
例)boolean値trueをBoolean値に変換するには、Boolean.valueOf(true)とする。

例)int値100をInteger値に変換するには、Integer.valueOf(100)とする。

例)boolean b1 = true; Boolean b2 = Boolean.valueOf(b1);

例)int i1 = 1; Integer i2 = Integer.valueOf(i1);

「対応する参照型」からプリミティブ型に変換するにはbooleanValue()、byteValue()、shortValue()、intValue()、longValue()、floatValue()、doubleValue()などのinstance methodを使います。

なぜJava1.9からこのような対応がされたかというと、コンストラクタで毎回新しく生成するよりも『valueOf』メソッドでキャッシュ利用をして欲しいから…という要旨のドキュメントを見ましたが、まだまだ理解不足の部分もあると思うので、もし間違いございましたらご指摘いただけると幸いです。


エンジニアファーストの会社
株式会社CRE-CO niwatori-program

この記事が気に入ったらサポートをしてみませんか?