4.

Java 型システム完全ガイド (プリミティブ/参照/Wrapper)

編集
この記事の要点
  • Java の型は大きく プリミティブ型 8 種参照型 (オブジェクト/配列) の 2 系統
  • プリミティブ: byte/short/int/long/float/double/char/boolean — スタックに値そのものを格納
  • 参照型は Wrapper クラス (Integer/Double 等) と AutoBoxing でプリミティブと相互変換
  • == はプリミティブでは値比較、参照型では参照アドレス比較 → 値の等価判定は equals()
  • モダン Java: var (Java 10+ 型推論)、record (Java 14+)、sealed (Java 17+)、generics、enum

Java の型システム概要

Java は静的型付け言語で、変数を宣言した時点で型が決まり、コンパイル時に型チェックが行われます。型は大きく 2 系統に分かれます。

分類格納場所初期値
プリミティブ型int, double, booleanスタック (値そのもの)0 / 0.0 / false
参照型 (オブジェクト)String, List, Userヒープ (アドレス参照)null
参照型 (配列)int[], String[]ヒープnull

プリミティブ型 8 種

ビット範囲リテラル例
byte8-128 〜 127byte b = 100;
short16-32,768 〜 32,767short s = 30000;
int32約 -21.4 億 〜 21.4 億int i = 100;
long64約 ±9.2 × 10^18long l = 100L;
float32IEEE 754 単精度float f = 1.5f;
double64IEEE 754 倍精度double d = 1.5;
char16U+0000 〜 U+FFFF (UTF-16)char c = 'A';
booleanJVM 依存true / falseboolean b = true;

リテラルの書き方

// 整数リテラル
int dec   = 100;        // 10進
int hex   = 0xFF;       // 16進 (255)
int oct   = 0755;       // 8進  (493)  ← 0始まりは8進!
int bin   = 0b1010;     // 2進  (10)   Java 7+
long big  = 10_000_000_000L;  // ★ アンダースコア区切り Java 7+, L 付与必須

// 浮動小数リテラル
double pi = 3.14;
float  f  = 3.14f;      // ★ f を付けないと double と解釈されコンパイルエラー
double exp = 1.5e10;    // 指数表記

// 文字・文字列
char ch  = 'あ';        // UTF-16
String s = "Hello";     // 参照型 (java.lang.String)

// boolean
boolean ok = true;

整数オーバーフロー

Java のプリミティブ整数はオーバーフローしても例外を投げません。ラップアラウンドして無音で間違った値になります。

int max = Integer.MAX_VALUE;     // 2147483647
int over = max + 1;              // -2147483648 ★ 警告なし
System.out.println(over);

// 安全な方法 (Java 8+): Math.addExact は例外を投げる
try {
    int safe = Math.addExact(max, 1);  // → ArithmeticException
} catch (ArithmeticException e) {
    System.out.println("overflow!");
}

// 64bit に拡張して回避
long big = (long) max + 1L;       // 2147483648 OK

// 任意精度
import java.math.BigInteger;
BigInteger huge = new BigInteger("99999999999999999999");

Wrapper クラスと AutoBoxing

プリミティブ型はオブジェクトではないため、List<int> のような Generics には使えません。Wrapper クラスでラップします。

プリミティブWrapper
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
// AutoBoxing : プリミティブ → Wrapper を自動変換
Integer wrapped = 100;        // 実際は Integer.valueOf(100)
List<Integer> nums = new ArrayList<>();
nums.add(1);                  // int → Integer 自動変換

// AutoUnboxing : Wrapper → プリミティブ
int n = wrapped;              // 実際は wrapped.intValue()

// ★ 罠1: null の AutoUnboxing → NullPointerException
Integer nullable = null;
int x = nullable;             // → NPE

Object/Wrapper の == と equals

参照型では ==参照アドレスの比較です。値の同一性は equals() を使います。

String a = new String("hello");
String b = new String("hello");

System.out.println(a == b);        // false (参照が違う)
System.out.println(a.equals(b));   // true  (値が同じ)

// ★ Integer キャッシュの罠
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);       // true  (-128〜127 はキャッシュ)

Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);       // false (キャッシュ外、別オブジェクト)
System.out.println(i3.equals(i4));  // true

// ★ null 安全な比較
Objects.equals(a, b);              // どちらも null OK

モダン Java の型機能

var (Java 10+) 型推論

// 右辺から型を推論。ローカル変数のみ。
var list = new ArrayList<String>();   // ArrayList<String>
var map  = new HashMap<String, Integer>();
var i    = 100;                       // int
var s    = "hello";                   // String

// ★ 禁止: フィールド・引数・戻り値には使えない
// var x;  // NG (初期化必須)
// var x = null;  // NG (null は推論不能)

record (Java 14+) 不変データクラス

// 1 行で final フィールド + getter + equals + hashCode + toString が生える
public record Point(int x, int y) {}

Point p = new Point(1, 2);
System.out.println(p.x());           // 1
System.out.println(p);                // Point[x=1, y=2]
System.out.println(p.equals(new Point(1, 2)));  // true

sealed (Java 17+) 継承制限

// 継承できるクラスを permits で限定
public sealed interface Shape permits Circle, Square, Triangle {}

public record Circle(double r) implements Shape {}
public record Square(double side) implements Shape {}
public record Triangle(double base, double h) implements Shape {}

// switch パターンマッチング (Java 21+)
double area = switch (shape) {
    case Circle c   -> Math.PI * c.r() * c.r();
    case Square s   -> s.side() * s.side();
    case Triangle t -> t.base() * t.h() / 2;
};

Generics

// 型パラメータで型安全なコレクション
List<String> names = new ArrayList<>();   // Java 7+ ダイヤモンド演算子
names.add("Alice");
String first = names.get(0);             // キャスト不要

// 自作 Generics
public class Box<T> {
    private T value;
    public T get() { return value; }
    public void set(T value) { this.value = value; }
}

// 境界
public <T extends Comparable<T>> T max(List<T> list) {
    return list.stream().max(Comparator.naturalOrder()).orElseThrow();
}

final と enum

// final 変数 = 再代入不可 (定数)
final int MAX = 100;

// final クラス = 継承不可
public final class Util { ... }

// final メソッド = override 不可
public final void run() { ... }

// enum = 型安全な定数集合
public enum Color {
    RED, GREEN, BLUE;

    public String hex() {
        return switch (this) {
            case RED   -> "#FF0000";
            case GREEN -> "#00FF00";
            case BLUE  -> "#0000FF";
        };
    }
}

Color c = Color.RED;
System.out.println(c.hex());     // #FF0000

FAQ

Q: int と Integer どちらを使う?
A: パフォーマンス重視なら int (プリミティブ)、Collection や Generics、null を許容したい場合は Integer。基本は int を優先。

Q: float と double はどちらが標準?
A: 通常は double を使います。float は 7 桁程度の精度しかなく、金融計算にはBigDecimalを使います。

Q: var を多用すべき?
A: ローカル変数で右辺から型が明らかなら可。new 演算子や明示的キャストとの併用が読みやすい。

編集
Post Share
子ページ
  1. 型の確認
  2. strからintへの型変換
  3. 辞書型を文字列に変換する方法
同階層のページ
  1. 基本的なルール
  2. 変数
  3. 演算子
  4. 標準ライブラリ
  5. 外部ライブラリ
  6. 制御構文
  7. リスト(配列)
  8. タプル
  9. セット
  10. 辞書(dict)
  11. クラスとメソッド
  12. 継承の概念と必要性
  13. 継承の構文
  14. コンストラクタ
  15. cookieの値の設定と取得
  16. 例外処理
  17. 例外を文字列で出力する方法
  18. httpリクエスト(curl)をする方法
  19. Responseオブジェクトの中身の確認
  20. 変数が空かどうか判定する方法
  21. タイムゾーンの設定と現在日時の取得と文字列化
  22. シングルクォーテーションとダブルクォーテーションの違い

最近更新/作成されたページ