【Java】基本型と参照型の使い分け|intとIntegerの違い・null安全な考え方
Javaを勉強していると、int と Integer のように、似たような型が出てきます。
例えば以下のような型です。
- int と Integer
- long と Long
- double と Double
- boolean と Boolean
最初は「どちらを使えばよいのか?」と迷いやすいポイントです。
この記事では、Javaの基本型と参照型の違い、実務での使い分け、nullチェックが必要になる場面についてまとめます。
- 1. 基本型と参照型の違い
- 2. よく使う基本型と参照型
- 3. intとIntegerの違い
- 4. 基本方針
- 5. 値が必ずある場合はintを使う
- 6. 未入力や未設定を表したい場合はIntegerを使う
- 7. 画面入力値はJava側でもチェックする
- 8. Integerをそのまま計算するとNullPointerExceptionになることがある
- 9. 計算前にnullの扱いを決める
- 10. Spring BootではDTOでバリデーションする
- 11. EntityではDBのNULL許可に合わせる
- 12. ListやMapではIntegerを使う
- 13. Integerの比較は注意
- 14. Integerを==で比較しない方がよい
- 15. booleanとBooleanの使い分け
- 16. Booleanのif文は注意
- 17. longとLongの使い分け
- 18. doubleとDoubleの使い分け
- 19. オートボクシングとアンボクシング
- 20. 使い分けの判断基準
- 21. レビュー観点
- 22. まとめ
- 23. 関連記事
基本型と参照型の違い
Javaの型は、大きく分けると以下の2種類があります。
- 基本型・・・int、long、double、boolean など
- 参照型・・・Integer、Long、Double、Boolean、String、クラス など
ざっくり言うと、基本型は値そのものを扱う型です。
一方、参照型はオブジェクトを扱う型です。
まずは、以下の違いを押さえておくと分かりやすいです。
| 分類 | 例 | null | 主な用途 |
|---|---|---|---|
| 基本型 | int | 入れられない | 必ず値がある数値 |
| 参照型 | Integer | 入れられる | 未入力・未設定を表したい数値 |
よく使う基本型と参照型
- int・・・整数
- Integer・・・nullを扱える整数
- long・・・大きい整数
- Long・・・nullを扱える大きい整数
- double・・・小数
- Double・・・nullを扱える小数
- boolean・・・true / false
- Boolean・・・true / false / null
intとIntegerの違い
まずは、一番よく迷う int と Integer の違いです。
int は基本型です。
// intは基本型
int age = 20;
Integer は参照型です。
// Integerは参照型
Integer age = 20;
大きな違いは、Integer は null を入れられることです。
// intにnullは入れられない
int age1 = null; // コンパイルエラー
// Integerにはnullを入れられる
Integer age2 = null;
基本方針
実務での基本方針は以下です。
- 値が必ずあるなら基本型を使う
- 未入力・未設定・DBのNULLを表したいなら参照型を使う
- 計算用の一時変数は基本型を使う
- ListやMapなどのコレクションでは参照型を使う
特に int と Integer では、以下のように考えると分かりやすいです。
| 場面 | 使う型 |
|---|---|
| 必ず値がある | int |
| 未入力の可能性がある | Integer |
| 計算用のカウンタ | int |
| 画面入力値 | Integer |
| DBのNULL許可カラム | Integer |
| Listに入れる数値 | Integer |
値が必ずある場合はintを使う
例えば、ループのカウンタや計算用の変数など、値が必ず存在する場合は int を使います。
// カウンタ
int count = 0;
// 合計値
int total = 0;
// 繰り返し処理のインデックス
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
このような値は、通常 null である必要がありません。
そのため、Integer にするより int の方がシンプルです。
未入力や未設定を表したい場合はIntegerを使う
画面入力値などで、未入力の可能性がある場合は Integer を使うことが多いです。
public class UserRequest {
// 画面で未入力の可能性がある場合
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
例えば年齢が未入力の場合、Integer であれば null として扱えます。
一方、int にしてしまうと、未入力を 0 と区別しにくくなります。
// intの場合、未入力なのか0なのか判断しにくい
int age = 0;
業務的に「未入力」と「0」は意味が違うことがあります。
その場合は、Integer を使って null を表現する方が自然です。
画面入力値はJava側でもチェックする
画面入力値について、JavaScriptでチェックしている場合でも、Java側でチェックするのが基本です。
JavaScriptのチェックは、あくまでユーザー向けの事前チェックです。
ブラウザの開発者ツールやAPIクライアントを使えば、画面を通さずにリクエストを送ることもできます。
そのため、Java側でも最終チェックを行います。
public void register(UserRequest request) {
Integer age = request.getAge();
if (age == null) {
throw new IllegalArgumentException("年齢は必須です");
}
int ageValue = age;
// ここから先はintとして計算できる
System.out.println(ageValue + 1);
}
重要なのは、Integer をそのまま何となく計算しないことです。
Integerをそのまま計算するとNullPointerExceptionになることがある
Integer は null になる可能性があります。
そのため、以下のようなコードは危険です。
Integer quantity = null;
// quantityがnullの場合、NullPointerExceptionになる
int total = quantity * 100;
一見すると普通の計算に見えますが、Integer を計算に使うと、Javaが内部的に int へ変換します。
この変換をアンボクシングといいます。
quantity が null の場合、int に変換できないため、NullPointerException が発生します。
計算前にnullの扱いを決める
画面入力値を計算に使う場合、まず null の扱いを決めます。
- 未入力ならエラーにする
- 未入力なら処理をスキップする
- 未入力なら0として扱う
- 未入力なら既存値を使う
この判断は、文法ではなく業務ルールです。
未入力ならエラーにする場合
必須入力の項目であれば、未入力の場合はエラーにします。
Integer quantity = request.getQuantity();
if (quantity == null) {
throw new IllegalArgumentException("数量は必須です");
}
int quantityValue = quantity;
int total = quantityValue * 100;
未入力なら処理をスキップする場合
任意入力で、入力されている場合だけ処理したいこともあります。
Integer point = request.getPoint();
if (point == null) {
// 未入力の場合は何もしない
return;
}
int addPoint = point + 100;
System.out.println(addPoint);
未入力なら0として扱う場合
未入力を0として扱ってよい場合は、計算前に0へ変換します。
Integer discount = request.getDiscount();
// nullの場合は0として扱う
int discountValue = discount == null ? 0 : discount;
int total = price - discountValue;
ただし、何でも null を 0 にすればよいわけではありません。
業務的に「未入力」と「0」が同じ意味である場合だけ、この方法を使います。
Spring BootではDTOでバリデーションする
Spring Bootを使っている場合、入力チェックはDTOで行うことが多いです。
例えば、数量が必須で1以上の場合は以下のように書けます。
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
public class OrderRequest {
@NotNull(message = "数量は必須です")
@Min(value = 1, message = "数量は1以上で入力してください")
private Integer quantity;
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
}
Controller側では、@Valid または @Validated を使います。
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@PostMapping("/orders")
public void createOrder(@Valid @RequestBody OrderRequest request) {
// @NotNullにより、ここに来た時点でquantityはnullではない前提にできる
int quantity = request.getQuantity();
int total = quantity * 100;
}
ただし、Serviceが他の処理からも呼ばれる場合は、Service側でも重要な条件をチェックすることがあります。
EntityではDBのNULL許可に合わせる
Entityでは、DBのカラムがNULLを許可するかどうかに合わせて型を決めることが多いです。
DBのカラムがNULLを許可する場合、Java側は Integer にします。
// DBでNULL許可されている場合
private Integer age;
DBのカラムがNOT NULLで、必ず値が入る設計であれば、用途によっては int でも問題ありません。
// DBでNOT NULL、かつ必ず値がある場合
private int quantity;
ただし、Entityではフレームワークとの相性や未設定状態を扱いたい都合で、ラッパークラスを使う設計もあります。
ListやMapではIntegerを使う
Javaのコレクションでは、基本型を直接使えません。
そのため、List や Map では Integer などの参照型を使います。
// List<int> は書けない
List<int> numbers = new ArrayList<>(); // コンパイルエラー
// Integerを使う
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
Map の場合も同じです。
Map<String, Integer> scoreMap = new HashMap<>();
scoreMap.put("Taro", 80);
scoreMap.put("Jiro", 90);
Integerの比較は注意
int 同士の比較であれば、== で問題ありません。
int a = 100;
int b = 100;
System.out.println(a == b); // true
一方、Integer は参照型です。
値の比較では、基本的に equals を使います。
Integer a = 1000;
Integer b = 1000;
System.out.println(a.equals(b)); // true
ただし、a が null の場合、a.equals(b) は NullPointerException になります。
nullの可能性がある場合は、Objects.equals を使うと安全です。
import java.util.Objects;
Integer a = null;
Integer b = 1000;
System.out.println(Objects.equals(a, b)); // false
Objects.equals は、どちらかが null でも安全に比較できます。
Integerを==で比較しない方がよい
Integer を == で比較すると、意図しない結果になることがあります。
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // falseになることがある
== は、値が同じかどうかではなく、同じオブジェクトかどうかを見ます。
そのため、Integer の値比較では Objects.equals を使う方が安全です。
if (Objects.equals(a, b)) {
System.out.println("同じ値です");
}
booleanとBooleanの使い分け
boolean と Boolean も考え方は同じです。
boolean は true か false のどちらかです。
boolean active = true;
boolean deleted = false;
Boolean は true、false、null を扱えます。
Boolean agreed = null;
例えば、以下の3状態を区別したい場合は Boolean が使えます。
- 同意する
- 同意しない
- 未回答
一方、単純に有効・無効だけを表したい場合は boolean で十分です。
// 有効・無効の2状態でよいならboolean
private boolean active;
Booleanのif文は注意
Boolean は null になる可能性があるため、そのままif文で使うと危険です。
Boolean active = null;
// activeがnullの場合、NullPointerExceptionになる
if (active) {
System.out.println("有効です");
}
nullの可能性がある場合は、以下のように書くと安全です。
Boolean active = null;
if (Boolean.TRUE.equals(active)) {
System.out.println("有効です");
}
Boolean.TRUE.equals(active) と書くことで、active が null でも安全に判定できます。
longとLongの使い分け
long と Long も、int と Integer と同じ考え方です。
必ず値があるなら long、未設定やDBのNULLを扱いたいなら Long を使います。
// 必ず値がある場合
long count = 100L;
// 未設定の可能性がある場合
Long userId = null;
ID項目では Long を使うことも多いです。
例えば、新規登録前のデータは、まだIDが発行されていない場合があります。
public class User {
// 新規登録前はnullの可能性がある
private Long id;
private String name;
}
doubleとDoubleの使い分け
double と Double も、nullを扱うかどうかで使い分けます。
// 必ず値がある小数
double rate = 0.1;
// 未入力の可能性がある小数
Double inputRate = null;
ただし、金額計算では double や Double は基本的に避けます。
小数の誤差が発生する可能性があるためです。
System.out.println(0.1 + 0.2); // 0.3ぴったりにならないことがある
金額を扱う場合は、BigDecimal を使うことが多いです。
BigDecimal price = new BigDecimal("100.50");
BigDecimal tax = new BigDecimal("1.10");
BigDecimal total = price.multiply(tax);
BigDecimal を作るときは、基本的に文字列から作るのが安全です。
オートボクシングとアンボクシング
Javaでは、基本型とラッパークラスの間で自動変換が行われることがあります。
基本型からラッパークラスへの変換をオートボクシングといいます。
// intからIntegerへ自動変換
Integer count = 10;
ラッパークラスから基本型への変換をアンボクシングといいます。
// Integerからintへ自動変換
Integer count = 10;
int countValue = count;
便利な仕組みですが、null の場合は注意が必要です。
Integer count = null;
// nullをintに変換しようとしてNullPointerExceptionになる
int countValue = count;
使い分けの判断基準
迷ったときは、以下の順番で考えると分かりやすいです。
- nullを表したいか?
- 画面入力で未入力の可能性があるか?
- DBでNULLを許可しているか?
- ListやMapに入れるか?
- 計算用の一時変数か?
判断を表にすると、以下のようになります。
| 場面 | 使う型 | 理由 |
|---|---|---|
| 計算用の一時変数 | int | nullが不要 |
| ループのカウンタ | int | nullが不要 |
| 画面の任意入力 | Integer | 未入力をnullで表せる |
| 画面の必須入力 | Integer + @NotNull | 入力チェック前はnullの可能性がある |
| DBのNULL許可カラム | Integer | DBのNULLを表せる |
| Listの要素 | Integer | コレクションは参照型を使う |
| true / falseだけでよいフラグ | boolean | 2状態で十分 |
| 未回答も表したいフラグ | Boolean | true / false / nullを扱える |
レビュー観点
コードレビューでは、以下のような点を確認するとよいです。
- nullになる意味がないのにIntegerを使っていないか
- nullになる可能性があるIntegerをそのまま計算していないか
- Integerを==で比較していないか
- Booleanをそのままif文で判定していないか
- 画面入力値をJava側でチェックしているか
- 金額計算にdoubleを使っていないか
危険な例
Integer quantity = request.getQuantity();
// quantityがnullの場合、NullPointerExceptionになる
int total = quantity * 100;
改善例
Integer quantity = request.getQuantity();
if (quantity == null) {
throw new IllegalArgumentException("数量は必須です");
}
int quantityValue = quantity;
int total = quantityValue * 100;
まとめ
Javaの基本型と参照型の使い分けでは、null を扱う必要があるかどうかが重要です。
- 値が必ずあるなら基本型を使う
- 未入力・未設定・DBのNULLを表したいなら参照型を使う
- 計算前にはnullの扱いを決める
- Integerをそのまま計算するとNullPointerExceptionになることがある
- ListやMapではIntegerなどの参照型を使う
- Booleanはtrue / false / nullの3状態を扱える
特に int と Integer では、以下の考え方が基本です。
// 値が必ずあるならint
int count = 0;
// 未入力や未設定を表したいならInteger
Integer age = null;
実務では、単に文法として覚えるのではなく、「この値はnullになる可能性があるか?」「nullの場合はどう扱うべきか?」を考えることが大切です。




