等価判定のNullPonterExceptionの回避方法〜

## 目的
- Javaの等価判定でNullPonterExceptionが発生してしまう場合の、回避方法を学ぼう

## 対象読者
- Java入門者
- Javaの等価判定をなんとなくでコードを記載している
- 等価判定ってなに？って人

## 等価判定とは
複数のオブジェクトが同じであればtrue,異なればfalseとなることを言います。
似たような比較に**等値判定**がありますが、使用方法は若干違います。


### 等価判定の使用方法
オブジェクトを生成して、実際に等価判定を行ってみました。

```java:Main.java
// 等価判定の場合
// 型：String
String s1 = "サンプル";
String s2 = "サンプル";
String s3 = "さんぷる";

System.out.println("s1,s2を比較" + s1.equals(s2));
System.out.println("s1,s3を比較" + s1.equals(s3));

```

```java:実行結果
true
false
```

###### 解説
**equals**で比較をしていることに注目してみよう。
まず、結果は**true**と**false**が表示されている。
オブジェクトの比較としては、最初は**s1**と**s2**で中身が両方**サンプル**、次の比較が**s1**と**s3**で中身が両方同一ではないことが確認できます。
それによって、オブジェクトの中身が同一であれば、**true**、同一でなければ**false**というのが想像できると思います。

### 等値判定の使用方法
オブジェクトを生成して、実際に等値判定を行ってみました。

```java:Main.java
// 等値判定の場合
// 型：String
String s1 = "サンプル";
String s2 = "サンプル";
String s3 = s1;

System.out.println("s1,s2を比較" + s1 == s2);
System.out.println("s1,s3を比較" + s1 == s3);

```

```java:実行結果
false
true
```
###### 解説
先ほど(equals)とは違う **==** で比較をしていることに注目してみよう。
まず、結果は**false**と**true**が表示されている。
オブジェクトの比較としては、最初は**s1**と**s2**で中身が両方**サンプル**、次の比較が**s1**と**s3**で中身が同一であることが確認できます。
ただ先ほどとは違い、オブジェクトの中身を比較しているようには見えません。
中身を比較していたら、先ほどの下記のような結果になるのは想定できるはずです。
>それによって、オブジェクトの中身が同一であれば、**true**、同一でなければ**false**というのが想像できると思います。

まず比較対象としては、オブジェクトの参照値となります。
オブジェクトの参照値となりますが、詳しくは話さないため、下記などを参考にしてください。

https://qiita.com/dsk_datadog/items/2e94c8722dc8f950e77c

参照値について、理解したら結果が**s1**と**s3**が**true**なのは理解できたはずです。

:::note
(補足）s1の参照値がs3に入ってるのは、上記コードから読み取れたはずです。
:::

さて、いよいよ**等価判定のNullPointerExceptionの回避方法**を説明しよう

### 等価判定〜NullPointerExceptionの回避方法
今回の方法としては、DTOに詰められているデータに対しての等価判定を行う場合となります。
(~~データベース設計に関しては、無視してください...~~)

```java:DTO.java
// 名前
private String name;

public String getname(){
    return name;
}

public void setname(String name){
    this.name = name;
}

```

```java:Logic.java
// インスタンス生成
private DTO dto;

// 等価判定
// 例えがおかしいですが...(nameに固定値が...
if(dto.getname().equals("山田"){ // ここで異常終了

}else{
    if(StringUtils.isEmpty(dto.getname())){
    }
}

```

この製造の仕方だと、テストケースで引っかかるとは思いますが、NullPointerの例外が発生します。
なぜかという比較しようとしている**dto.getname()** がnullが来た場合、比較できないので例外が発生しますよね...
> ~~自分はもちろん経験済みですが~~

回避策としては、下記があるのかなと思っています。（それ以外思い浮かばず...)
1. 固定値がくることがあれば定数を左に持ってくる
2. Nullチェックを先に判定する。
3. 『Objects.equals()』を使用する
4. 『StringUtils.equals()』を使用する。

### 定数を左 
```diff_java:Logic.java
// インスタンス生成
private DTO dto;

// 等価判定
+ // 1の場合
+ final String KEY_WORD = "山田";
+ if(KEY_WORD.equals(dto.getname());
- // 例えがおかしいですが...(nameに固定値が...
- if(dto.getname().equals("山田"){ 
 // 処理記述
}else{
     if(StringUtils.isEmpty(dto.getname())){
     // 処理記述
    }
}

```

### nullチェックを先に行う
```diff_java:Logic.java
// インスタンス生成
private DTO dto;

// 等価判定
+ // 2の場合
+ if(StringUtils.isEmpty(dto.getname())){
 // 本来は処理記述
}else{
- // 例えがおかしいですが...(nameに固定値が...
    if(dto.getname().equals("山田"){
     // 処理記述
    }
}

```

### Objects.equalsの使用
```diff_java:Logic.java
// インスタンス生成
private DTO dto;

// 等価判定
+ // 3の場合
- // 例えがおかしいですが...(nameに固定値が...
-   if(dto.getname().equals("山田"){
+   if(Objects.equals(dto.getname(),"山田"){   
     // 処理記述
    }
}

```

### StringUtils.equalsの使用
```diff_java:Logic.java
// インスタンス生成
private DTO dto;

// 等価判定
+ // 4の場合
- // 例えがおかしいですが...(nameに固定値が...
-   if(dto.getname().equals("山田"){
+   if(StringUtils.equals(dto.getname(),"山田"){   
     // 処理記述
    }
}

```

| 番号 | 想定される使用方法 | 
|:-----------|------------:|
| 1       | 予め判定を行う片方が"固定値"であれば、使用できる想定です。 | 
|  2    |  空,nullチェックを先に行う理由は、ぬるぽ回避ぐらいですかね...     | 
| 3       | 比較する対象がどちらも"null"要素が入る場合、使用するのが望ましいんですかね。        | 
| 4         | 比較する対象がどちらも"null"要素が入る場合、使用するのが望ましいんですかね。          | 

~~3番と4番の違いがそこまで理解できてなく....~~

一旦はこんな形で回避できるの中〜って思っています。
最後まで見ていただき、ありがとうございます。
※誤りが合った場合、適宜修正は行っていきます。