新人エンジニアが最初に知っておきたい演算子の使い方

なぜ、演算子が重要なのか、その理由。

この記事では、弊社の新人エンジニア研修の参考にJavaを解説します。

前回は変数とデータ型について解説しました。

今回は演算子を解説します。コンピュータ【Computer】の語源は「一緒に計算してくれる人」です。したがって計算処理はプログラムの本質的な役割といえるでしょう。

Javaには様々な演算子がありますが、ここではその中でも

・算術演算子
・キャスト演算子

を解説します。

・関係演算子
・条件演算子
・三項演算子

については、次回の4.条件分岐と判定条件でお話しします。

1.算術演算子

四則演算(+-×÷)などをする演算子を算術演算子といいます。一例として、Javaで足し算をする場合を見てみましょう。

package chap03;

public class Example01 {
    public static void main(String[] args) {
        System.out.println(5 + 2);
    }
}

このプログラムを実行すると標準出力には「7」と出力されます。この時の、「+」記号を演算子(オペレータ)と呼び、5や2のことをオペランドと呼びます。また、「5 + 2」 は7という値を持つため式と呼ばれます。さらに、 System.out.println(5 + 2);のように;(セミコロン)までを文というのでしたね。

演算子自体はいろいろな種類がありますが、この場合の「+記号」は足し算を意味するので特に算術演算子といいます。

ここで、Javaの算術演算子をまとめましょう。

算術演算子演算内容式の例結果(値)
+足し算5 + 27
-引き算5 - 23
*掛け算5 * 210
/割り算5 / 22 
%剰余(余りを求める)5 % 21
表3.1 算術演算子

ここで、5 / 2 = 2.5 ではなく、2となることに注目してください。整数同士の演算では結果も整数になるからです。小数点以下は切り捨てられるわけです。Javaは式も型を持つのです。(例えば、1 + 2.0 はdoubleといったように)

また、数学でも掛け算と割り算(そして剰余)は、足し算と引き算より優先され先に計算されました。Javaでも同じです。

・以下のようなプログラムを書くと実行結果はどうなりますか?

System.out.println(1 + 5 % 2);

あなたの答え:
  • ()を使って以下のように優先順位を変更してみましょう。以下のようなプログラムを書くと実行結果はどうなりますか?

System.out.println((1 + 5) % 2);

あなたの答え:

2.変数を含む式

式には変数を含めることもできます。

以下のExample02は2から順に5まで1つづ増やした数を表示します。

package chap03;

public class Example02 {

    public static void main(String[] args) {
        int i = 1;
        int j = i + 1;
        System.out.println(j);

        j = j + 1;
        System.out.println(j);

        System.out.println(j += 1); //短縮表記の例1

        System.out.println(++j); //短縮表記の例2 j+=1; System.out.println(j);と同じ

        System.out.println(j++); //短縮表記の例3 System.out.println(j); j+=1;と同じ

        System.out.println(j);
    }
}

<実行結果>

2
3
4
5
5
6


間違えやすいので繰り返しですが、上記の

int j = i + 1;

という式は数学の左辺と右辺が等しいという意味ではありません。は変数jにi+1という式の値を代入するという意味です。=は代入を意味するということは新人エンジニアのみなさんが間違えやすいところなので気をつけましょう。

ちなみに等しいは「==」で表すということをこのあとすぐ学びます。

また、上記のように変数を使った式は短縮表記も可能です。

j += 1

複合代入演算子といいます。

複合代入演算の作り方は以下の通りです。

複合代入演算子の書き方
図3.1 複合代入演算子の書き方

以下に加算以外の例を示します。

number -= 2; 
number *= 2; 
number /= 2; 
number %= 2; 

変数名が長くなれば短縮効果が効いてくる書き方です。

少し練習してみましょう。

通常の書き方複合代入演算子を使った書き方値(numberは2で初期化されているものとする)
number = number - 3;-
number = number * 3;6
number = number / 3;
number = number % 3;
表3.2 複合代入演算子の問題

また、短縮表記の例2のような書き方で1増やす処理をインクリメントといいます。さらに、1減らすデクリメントというものもあります。(例:--j)

インクリメント、デクリメントにはオペランドに対してどこに書かれるかによって前置と後置があります。前置は、式の中でオペランドの値は増やされたり減らされたりした“後”の値が用いられます。後置は、式の中でオペランドの値は増やされたり減らされたりする“前”の値が用いられます。

つまり、

System.out.println(++j);

j = j + 1;
System.out.println(j);

と同じです。

また、

System.out.println(j++);

System.out.println(j);
j = j + 1;

と同じです。

なお、

i++;
++i;

のように単独で使った場合は前置、後置は同じ結果になります。

3.変数の型と代入の制限

前回、変数には型があって入れられる数値の範囲が違うということを学びました。では、例えばdouble型の変数にint型の整数を入れるとどうなるでしょうか?また、その逆はどうでしょうか?

以下のExample03を実行してみましょう。

package chap03;

public class Example03 {
    public static void main(String[] args) {
        double d = 1;
        System.out.println(d);
    }
}

<実行結果>

1.0

エラーにはなりませんでした。

これを暗黙的な型変換といいます。「大は小を兼ねる」ということで、より大きな型への代入は暗黙の型変換がされます

しかし、以下のようなプログラムはエラーになります。

package chap03;

public class Example04 {
    public static void main(String[] args) {
        int i = 3.14;
        System.out.println(i);
    }
}

<実行結果>

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
型の不一致: double から int には変換できません


at chap03.Example04.main(Example04.java:6)

このように精度が失われる可能性のある代入はできません。しかし、もし、プログラマーが精度が失われても構わないと考えた場合にも代入できないとすると不都合です。それを可能にするのがキャスト(cast)演算子です。

4.キャスト演算

より小さな型への代入を可能にするキャスト演算は()「丸カッコ」を使います

以下のExample05を実行してみましょう。

package chap03;

public class Example05 {
    public static void main(String[] args) {
        int i = (int)3.14;
        System.out.println(i);
    }
}

今回は3と表示されました。このような演算をキャスト演算または型変換と呼びます。

よく、映画などで俳優のキャスティング【casting】という言い方をします。

新人エンジニア研修でキャストを映画の例えで説明

悪役にキャスティングされた俳優は悪役としてふるまい、ヒーローにキャスティングされたらヒーローとしてふるまう。

違った型としてふるまうように指定するのがこのキャスト演算です。あとでオブジェクトのキャストという応用のお話も出てきますので、しっかり押さえてください。

では、例えば、異なる型の変数や値が混在している演算の結果はどうなるでしょうか?

答えは、最も大きい型に統一された計算結果になります。

package chap03;

public class Example06 {
    public static void main(String[] args) {
        int i = 1;
        double d = 3.1;
        System.out.println(i + d);
    }
}

<実行結果>

4.1

intとdoubleの算術演算はdouble型の結果になりました。

ここで新人エンジニアの皆さんが気を付けたいのは整数同士の割り算です。

System.out.println(5 / 2);

int同士の割り算の結果は「2」になりました。

もしも、2.5と表示させたいのであれば、どちらかのオペランドをdoubleにキャストする必要があります。

例えばこんな風に、

System.out.println(5 / (double)2);

です。

下図3.2に暗黙の型変換といってキャストが必要ない場合とキャストが必要な場合をまとめています。

上の型から下の型への代入には(地球の重力に引かれるように)暗黙の型変換が働きますからキャストは不要です。しかし、下から上の型への代入にはキャストが必要です。

暗黙の型変換のルール
図3.2 暗黙の型変換のルール

なお、上記の図の横幅はビット数をイメージしています。char型が独自の位置を占めていますね。

同じビット数のはずのchar型とshort型の間で暗黙の型変換が働かないのはなぜだか分かりますか?

char型にはマイナスが無かったこと【unsigned】を思い出してください。なぜ、ビット数の少ないfloat型がlong型を受け入れられるのかは、float型が指数を生かした浮動小数点表現だったことを思い出してください。

5.ArithmeticException

以下のように割る数を0にしたらどうなるでしょうか?

package chap03;

public class Example07 {
    public static void main(String[] args) {
        System.out.println(5 / 0);
    }
}
あなたの予想:
実行結果:

このように例外【Exception】が表示されます。

0で割った結果は数学上未定義ですので、こうなります。(ちなみに5/0.0のように浮動小数点数の0除算の結果はInfinityとなります)

どうか例外を嫌いにならないで下さい。例外表示は皆さんの味方です。

例えば、Java言語の先祖にあたるC言語では0除算の結果は文字通り未定義です。未定義というのは、実行時のメモリの状況により異常停止したり意味不明の結果が出力されたりといった事が起こるのです。しかし、Javaではコンパイラが実行前に教えてくれるのです。例外は新人エンジニアの皆さんの味方です。

もちろん、意図的に0で割る計算をすることはないでしょう。しかし、割る数が変数ですと意図せず0で割ってしまうこともあります。そんな時は、「もしも、割る数が0だったら処理をしない」といった条件分岐や、エラーが発生しても処理を止めない例外処理といったことが必要になります。この点は後述します。

6.文字列の結合に使用する「+」

文字列はString型の変数に代入できます。

package chap03;

public class Example08 {
    public static void main(String[] args) {
        String str = "Hello World";
        System.out.println(str);
    }
}

文字列は別の章で詳しく学びます。

また、文字列を連結するには「+」演算子を使います。

package chap03;

public class Example09 {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = " World";
        System.out.println(str1 + str2);
    }
}

<実行結果>

Hello World

しかし、「+」演算子は足し算でも使いましたね。文字列と数値が混在する場合はどうなるでしょうか?

package chap03;

public class Example10 {
    public static void main(String[] args) {
        System.out.println("1 + 2 = " + 1 + 2);
        System.out.println(1 + 2 + "は 1 + 2 の答えです");
    }
}

<実行結果>

1 + 2 = 12
3は 1 + 2 の答えです


左から順番に結合されています。

算術演算子は左結合
図3.3 算術演算子は左結合

算術演算子は左結合というルールがあります。そのため、文字列に連結された時点で全体が文字列と評価されるので上記の結果になるのです。

文字列と算術演算が混在する+演算には注意が必要です。更に文字列を+で結合することはパフォーマンスが悪いことにも注意しましょう。新人エンジニアの皆さんには少々応用的な知識ですが記憶の片隅にとどめてください。

7.文字と文字列の扱いで詳しくお話ししますが、ここでも簡単に触れておくと文字列は1度生成すると内部の文字列を変更できません。そのため文字列を連結する時には新しい文字列が生成されるのです。数回の結合であれば問題になりませんが、何千何万回となると影響があります。そのような場合にはStringBuilderクラスまたは、StringBufferクラスのappend()メソッドの使用を推奨します。

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□ 算術演算子は+ - * / % の5種類である

□ =は代入を意味する

□ より大きな型への代入は暗黙の型変換がされ、より小さな型への代入にはキャストが必要である

□ 算術演算子は左結合というルールがあるため文字列と算術演算が混在する出力には注意が必要である

まとめができたら、アウトプットとして演習問題にチャレンジしましょう。

以上、今回は「演算子でプログラムに計算させる」方法について見てきました。

次回は、「条件分岐で場合に応じた処理ができるようになる」です。

演算子でプログラムに計算させる 最後までお読みいただきありがとうございます。