Javaの演算子を勉強してみました


## はじめに
現在、私が進行しているJavaの勉強会でまとめた内容です。

内容に不正確な点があれば、ご指摘いただけるとありがたいです。

https://github.com/whiteship/live-study/issues/3

- 韓国人として、日本語とコンピュータの勉強を同時に行うために、ここに文章を書いています
- 翻訳ツールの助けを借りて書いた文章なので、誤りがあるかもしれません


# 演算子
- 算術演算子
- ビット演算子
- 関係演算子
- 論理演算子
- instanceof
- 代入(=)演算子
- アロー(->)演算子
- 三項演算子
- 演算子の優先順位
- Java 13. switch演算子



## Javaで提供される演算子
演算子と被演算子とは？
- 演算子: 演算に使用される記号（+, - など）
- 被演算子: 演算に使用される対象（1, 2, a など）

**ほとんどの演算**はプリミティブ型でのみ使用可能です。

- Stringクラスは除く

```java
String str1 = "Hello, ";
String str2 = "World!";
String result = str1 + str2;  // 文字列連結演算
System.out.println(result);  // "Hello, World!"
```

- Wrapperクラスは除く（AutoBoxing）を通じてプリミティブ型に変換された後に計算される

```java
Integer a = 10;
Integer b = 20;
int sum = a + b;  // AutoBoxingを通じてプリミティブintに変換された後に演算
```

- instanceof演算子は除く


### 演算子の種類
```
=   >   <   !   ~   ?   :   ->
==  >=  <=  !=  &&  ||  ++  --
+   -   *   /   &   |   ^   %   <<   >>   >>>
+=  -=  *=  /=  &=  |=  ^=  %=  <<=  >>=  >>>=
```

## 算術演算子

| 算術演算子   | 説明                              |
|-------------|-----------------------------------|
| +           | 加算（文字列の連結にも使用される） |
| -           | 減算                              |
| *           | 乗算                              |
| /           | 除算                              |
| %           | 剰余                              |


```java
class ArithmeticDemo {

    public static void main (String[] args) {

        int result = 1 + 2;
        // result is now 3
        System.out.println("1 + 2 = " + result);
        int original_result = result;

        result = result - 1;
        // result is now 2
        System.out.println(original_result + " - 1 = " + result);
        original_result = result;

        result = result * 2;
        // result is now 4
        System.out.println(original_result + " * 2 = " + result);
        original_result = result;

        result = result / 2;
        // result is now 2
        System.out.println(original_result + " / 2 = " + result);
        original_result = result;

        result = result + 8;
        // result is now 10
        System.out.println(original_result + " + 8 = " + result);
        original_result = result;

        result = result % 7;
        // result is now 3
        System.out.println(original_result + " % 7 = " + result);
    }
}
```

- 결과 

```
1 + 2 = 3
3 - 1 = 2
2 * 2 = 4
4 / 2 = 2
2 + 8 = 10
10 % 7 = 3
```

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

- 割り算演算子 `/` と剰余演算 `%` は、**オペランドが整数のとき**に0で割ることはできず、`ArithmeticException` エラーが発生します
- 整数型の割り算 (`/`) で **0で割る場合**、`ArithmeticException` が発生します。一方で、**浮動小数点数**の場合は例外が発生せず、**Infinity** または **NaN** の値が返されます


- ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/1b10898a-4264-3696-15fc-1d709ddcf656.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/f3bc397a-e94e-fd16-3090-424aafe8f780.png)


Stringの演算もUnicodeで可能です

```java
// Stringと他の型の+演算
int number = 42;
String result = "Number: " + number;  // 他の型も自動的にStringに変換

String str = "a";
System.out.println(str + 2);      // "a2" 出力
System.out.println("a" + 2);      // "a2" 出力

char c = 'a';
System.out.println((char)(c + 2));  // 'c' 出力
// 'a'(97) + 2 = 99('c')

// Stringに変換したい場合
String result = String.valueOf((char)(c + 2));  // "c"
```

## 単項演算子
| 単項演算子 | 説明                                 |
|------------|--------------------------------------|
| +          | 正数を表す（省略可能だが、明示可能） |
| -          | 負数を表す                           |
| ++         | 1増加                               |
| --         | 1減少                               |
| !          | 反対（論理否定演算子）              |



```java
class UnaryDemo {

    public static void main(String[] args) {

        int result = +1;
        // result is now 1
        System.out.println(result);

        result--;
        // result is now 0
        System.out.println(result);

        result++;
        // result is now 1
        System.out.println(result);

        result = -result;
        // result is now -1
        System.out.println(result);

        boolean success = false;
        // false
        System.out.println(success);
        // true
        System.out.println(!success);
    }
}
```
増加/減少演算子は被演算子の前後でも適用が可能です。

```java
class PrePostDemo {
    public static void main(String[] args){
        int i = 3;
        i++;
        // prints 4
        System.out.println(i);
        ++i;			   
        // prints 5
        System.out.println(i);
        // prints 6
        System.out.println(++i);
        // prints 6
        System.out.println(i++);
        // prints 7
        System.out.println(i);
    }
}
```

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

```java
int a = -10;
System.out.println(-x); // 10を出力
```

## 比較演算子（等価、関係演算子）

オブジェクトのアドレスを比較します。（参照型でも同様です）

| 比較演算子 | 説明                              |
|------------|-----------------------------------|
| ==         | 2つの値が同じときに真             |
| !=         | 2つの値が異なるときに真           |
| >          | 最初の値が2番目の値より大きいときに真 |
| >=         | 最初の値が2番目の値以上のときに真 |
| <          | 最初の値が2番目の値より小さいときに真 |
| <=         | 最初の値が2番目の値以下のときに真 |



```java
class ComparisonDemo {

    public static void main(String[] args){
        int value1 = 1;
        int value2 = 2;
        if(value1 == value2)
            System.out.println("value1 == value2");
        if(value1 != value2)
            System.out.println("value1 != value2");
        if(value1 > value2)
            System.out.println("value1 > value2");
        if(value1 < value2)
            System.out.println("value1 < value2");
        if(value1 <= value2)
            System.out.println("value1 <= value2");
    }
}

```

- 결과
```java
value1 != value2
value1 <  value2
value1 <= value2
```


```java
int a = 5 
a ++ == -- a // true
```

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html

## 論理演算子

2つ以上のboolean表現式を評価して結果を生成する演算子

| 条件演算子 | 説明                                      |
|------------|-------------------------------------------|
| &&         | (AND) 両方の条件が真のときのみ結果が真    |
| \|\|       | (OR) どちらか一方の条件が真であれば結果が真 |
| ?:         | 三項演算子 条件 ? 真の場合の値 : 偽の場合の値 |

- 左側がfalseの場合、右側は評価されません

```java
// aがfalseの場合、bは評価されません
if (a && b) 

// aがtrueの場合、bは評価されません
if (a || b)
```

- `&` と `|` は常に両側を評価します

```java
// aがfalseでもbを評価します
if (a & b)  

// aがtrueでもbを評価します
if (a | b)
```
- `||`、`&&`とは異なり、必ず両辺の値を比較するため、パフォーマンス上`&&`、`||`の方が有利です


### 三項演算子

```java
result = (condition) ? expression1 : expression2;
```
- `condition`: 評価する条件（trueまたはfalseに評価される式）
- `expression1`: `condition`が`true`のときに実行される式
- `expression2`: `condition`が`false`のときに実行される式


```java
 条件 ? 真のときの値 : 偽のときの値
 ```
以下のような形式で使用することもできますが、演算は効率的ではありません。  
なぜなら、必ず右側の値までチェックする必要があるからです。

 ```java
 int score = 85;
String grade = (score >= 90) ? "A" : 
               (score >= 80) ? "B" : 
               (score >= 70) ? "C" : 
               (score >= 60) ? "D" : "F";
```

- メソッドの実行結果を条件として使用することもできます

```java
String name = user != null ? user.getName() : "Unknown";

// someMethodの結果がtrueの場合に実行される処理
boolean hasPermission = isAdmin() ? true : checkPermission(user);
```


## ビット演算子
| 演算子 | 説明                                      |
|--------|-------------------------------------------|
| &      | 両方のビットが1のときのみ1 (AND)         |
| \|     | どちらかのビットが1であれば1 (OR)         |
| ^      | ビットが異なるとき1 (XOR)                |
| ~      | ビット値を反転 (NOT)                      |
| <<     | 左にビットを移動 (LEFT SHIFT)             |
| >>     | 右にビットを移動 (RIGHT SHIFT)           |
| >>>    | 右にビットを移動（0で埋める）           |

```java
// & (AND 演算)
5 の表現:  00000000 00000000 00000000 00000101
3 の表現:  00000000 00000000 00000000 00000011
5 & 3:  00000000 00000000 00000000 00000001  // 結果: 1（両方が1のビットのみ1）

// | (OR 演算)
5 の表現:  00000000 00000000 00000000 00000101
3 の表現:  00000000 00000000 00000000 00000011
5 | 3:  00000000 00000000 00000000 00000111  // 結果: 7（どちらかが1なら1）

// ^ (XOR 演算)
5 の表現:  00000000 00000000 00000000 00000101
3 の表現:  00000000 00000000 00000000 00000011
5 ^ 3:  00000000 00000000 00000000 00000110  // 結果: 6（異なるビットは1）

// << (左シフト)
5 の表現:  00000000 00000000 00000000 00000101
5 << 1:   00000000 00000000 00000000 00001010  // 結果: 10（左に1ビットシフト、右は0）

// >> (符号付き右シフト)
-8 の表現: 11111111 11111111 11111111 11111000
-8 >> 1:   11111111 11111111 11111111 11111100  // 結果: -4（符号ビット1を維持）

// >>> (符号なし右シフト)
-8 の表現:  11111111 11111111 11111111 11111000
-8 >>> 1:   01111111 11111111 11111111 11111100  // 結果: 2147483644（必ず0で埋める）
```
- ビット演算は、2進数に基づいてメモリや演算速度の面で利点をもたらします

## instanceof 演算子
- オブジェクトが指定された型と一致するかを確認する（オブジェクトの型を確認する演算子）
- instanceofを使用する際、nullはクラスや型ではないため、常にfalseと判断されます
- プリミティブ型は演算できません


```java
String str = null;
System.out.println(str instanceof String); // false

int number = 42;
if (number instanceof Integer) { } // コンパイルエラー
```

```java
class InstanceofDemo {
    public static void main(String[] args) {

        Parent obj1 = new Parent();
        Parent obj2 = new Child();

        System.out.println("obj1 instanceof Parent: "
            + (obj1 instanceof Parent));
        System.out.println("obj1 instanceof Child: "
            + (obj1 instanceof Child));
        System.out.println("obj1 instanceof MyInterface: "
            + (obj1 instanceof MyInterface));
        System.out.println("obj2 instanceof Parent: "
            + (obj2 instanceof Parent));
        System.out.println("obj2 instanceof Child: "
            + (obj2 instanceof Child));
        System.out.println("obj2 instanceof MyInterface: "
            + (obj2 instanceof MyInterface));
    }
}

class Parent {}
class Child extends Parent implements MyInterface {}
interface MyInterface {}
```

```java
obj1 instanceof Parent: true
obj1 instanceof Child: false
obj1 instanceof MyInterface: false
obj2 instanceof Parent: true
obj2 instanceof Child: true
obj2 instanceof MyInterface: true
```

## アロー(->)演算子
- アロー(->)演算子（ラムダ式）はJava 8で導入
- 関数型プログラミングを表現
- 副作用(side-effect)の発生を最小限に抑えるため

```java
// Bad: 副作用があるコード
int sum = 0;
list.forEach(num -> {
    sum += num;  // 外部変数を変更（副作用）
});

// Good: 副作用のないコード
int sum = list.stream()
             .reduce(0, (a, b) -> a + b);  // 外部変数の変更なし


```java
// アロー演算子(->)を使用したラムダ式のさまざまな形
(引数) -> { 実行文; }  // 基本形
() -> System.out.println("Hello")  // 引数なし
x -> x * x  // 引数が1つのときは括弧省略可能
(a, b) -> a + b  // 引数が複数
```
その他の条件や使用法については、ラムダ式の別セクションで取り扱います。

## 代入演算子

`=` `+=` `-=` `*=` `/=` `%=` `&=` `^=` `|=` `<<=` `>>=` `>>>=`

- 右側の値を左側の被演算子に割り当てます

```java
int a = 0;
long b = 1L;
double c = 0.4;
```

```java
Point point1 = new Point(23, 94);
Point point2 = point1;

point2.x = 50;
System.out.println(point1.x); // 出力: 50

System.out.println(point1 == point2); // true
```


```java
int a = b = c = d;
```
- 右から左に値が反映されます
- 上記の式では、`d`の値が反映されます

```java
int x = 5;
x += x++; // 11
```
```java
int x = 5;
x += ++x; // x = 12
```

## 演算子のまとめ

### 単純代入演算子
- `=` : 単純代入演算子

**算術演算子**

- `+` : 加算演算子（文字列の連結にも使用）
- `-` : 減算演算子
- `*` : 乗算演算子
- `/` : 除算演算子
- `%` : 剰余演算子

**単項演算子**

- `+` : 正数符号演算子（通常、数値はこの符号なしでも正数）
- `-` : 負数符号演算子；式を負数にする
- `++` : インクリメント演算子；値を1増加させる
- `--` : デクリメント演算子；値を1減少させる
- `!` : 論理否定演算子；boolean値を反転させる

**等価および関係演算子**

- `==` : 等しい
- `!=` : 等しくない
- `>` : より大きい
- `>=` : 以上
- `<` : より小さい
- `<=` : 以下

**条件演算子**

- `&&` : 条件付きAND（論理積）
- `||` : 条件付きOR（論理和）
- `?:` : 三項演算子（if-then-elseの省略形）

**型比較演算子**

- `instanceof` : オブジェクトを特定の型と比較する

**ビットおよびビットシフト演算子**

- `~` : 単項ビット反転
- `<<` : 符号付き左シフト
- `>>` : 符号付き右シフト
- `>>>` : 符号なし右シフト
- `&` : ビットAND
- `^` : ビット排他的OR（XOR）
- `|` : ビット包含OR

## 演算子の優先順位

| 優先順位 | 演算子の種類           | 演算子                         | 方向          |
|----------|------------------------|--------------------------------|---------------|
| 1        | 後置演算子             | expr++, expr--                 | →（左から右）|
| 2        | 単項演算子             | ++expr, --expr, +expr, -expr, ~, ! | →（左から右）|
| 3        | 乗算/除算演算子        | *, /, %                        | →（左から右）|
| 4        | 加算/減算演算子        | +, -                           | →（左から右）|
| 5        | シフト演算子           | <<, >>, >>>                    | →（左から右）|
| 6        | 関係演算子             | <, >, <=, >=, instanceof       | →（左から右）|
| 7        | 等価演算子             | ==, !=                         | →（左から右）|
| 8        | ビット論理積           | &                              | →（左から右）|
| 9        | ビット排他的論理和     | ^                              | →（左から右）|
| 10       | ビット論理和           | \|                             | →（左から右）|
| 11       | 論理 AND               | &&                             | →（左から右）|
| 12       | 論理 OR                | \|\|                           | →（左から右）|
| 13       | 三項演算子             | ? :                            | →（左から右）|
| 14       | 代入演算子             | =, +=, -=, *=, /=, %=, &=, ^=, \|= | ←（右から左）|


- `()`で囲むことで演算の順序を変更することができます


## Java 13. switch演算子

従来のswitch文には以下の問題が発生します。
1. break文を省略すると`フォールスルー（fall-through）`が発生する
2. 値を返すことができない
3. 開発者がすべてのケースをチェックしたかをコンパイル時に確認できない

```java
public class SwitchDemo {
    public static void main(String[] args) {

        int month = 8;
        String monthString; // 外部に値を宣言
        switch (month) {
            // caseを省略する危険がある
            case 1:  monthString = "January";
                     break;
            case 2:  monthString = "February";
                     break;
            case 3:  monthString = "March";
                     break;
            case 4:  monthString = "April";
                     break;    // breakを省略するとfall-through問題発生
                     // 以下すべての月が出力される
            case 5:  monthString = "May";
                     break;
            case 6:  monthString = "June";
                     break;
            case 7:  monthString = "July";
                     break;
            case 8:  monthString = "August";
                     break;
            case 9:  monthString = "September";
                     break;
            case 10: monthString = "October";
                     break;
            case 11: monthString = "November";
                     break;
            case 12: monthString = "December";
                     break;
            default: monthString = "Invalid month";
                     break;
        }
        System.out.println(monthString);
    }
}
```


このように、switchでは`->`演算子を使用でき、その利点は以下の通りです。

1. より簡潔な構文を提供
2. break文が不要
3. fall-throughの危険がない（2番と関連し、returnがなくても自動で終了）
4. 値を返すことが可能
5. コンパイラがすべてのcaseが処理されているかを検査（完全性チェック）
6. `yield`を使用してswitchの結果を返すこともできます

```java
public class SwitchDemo {
    public static void main(String[] args) {
        int month = 8;
        
        // 基本的なアロー演算子の使用
        // 値を直接割り当て
        // すべてのcaseが処理されていないとコンパイルエラーが発生
        // break文が不要でfall-throughの危険がない
        String monthString = switch (month) {
            case 1 -> "January";
            case 2 -> "February";
            case 3 -> "March";
            case 4 -> "April";
            case 5 -> "May";
            case 6 -> "June";
            case 7 -> "July";
            case 8 -> "August";
            case 9 -> "September";
            case 10 -> "October";
            case 11 -> "November";
            case 12 -> "December";
            default -> "Invalid month";
        };

        // 方法2: 複数のcaseをまとめて処理
        String season = switch (month) {
            case 12, 1, 2 -> "Winter";
            case 3, 4, 5 -> "Spring";
            case 6, 7, 8 -> "Summer";
            case 9, 10, 11 -> "Fall";
            default -> "Invalid month";
        };

        System.out.println(monthString);
        System.out.println(season);
    }
}
```

```java
// 従来の方法: 外部変数が必要
String result;  // 外部変数の宣言が必要
switch(value) {
    case 1:
        doSomething();
        result = "ONE";  // 外部変数に代入
        break;
}

// 新しい方法: 直接返す
String result = switch(value) {  // 直接代入が可能
    case 1 -> {
        doSomething();
        yield "ONE";    // 直接返却
    }
};
```

## リファレンス
https://catch-me-java.tistory.com/30

https://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html