1.

Java の型変換|キャストで壊れる値・parseInt・オートボクシング

編集
この記事の要点
  • 小さい型から大きい型へは自動で変換される(拡大変換)。逆はキャストが必要
  • キャストで縮めると値が黙って壊れる(byte)200-56 になる
  • 整数どうしの割り算は整数のまま5 / 22.5 ではなく 2
  • 文字列 → 数値は Integer.parseInt()失敗すると NumberFormatException
  • オートボクシングで Integernull だと、取り出す瞬間に NullPointerException

プリミティブ型の変換

// 拡大変換(自動): byte → short → int → long → float → double
int i = 100;
long l = i;              // 自動
double d = l;            // 自動
char c = 'A';
int code = c;            // 65   char → int も自動

// 縮小変換(キャストが必要)
double price = 1980.9;
int p = (int) price;     // 1980   小数は切り捨て(四捨五入ではない)

long big = 10_000_000_000L;
int small = (int) big;   // 1410065408   桁があふれて壊れる

int n = 200;
byte b = (byte) n;       // -56        byte は -128〜127
サイズ範囲
byte8 bit-128 〜 127
short16 bit-32,768 〜 32,767
int32 bit約 ±21 億
long64 bit約 ±922 京
float32 bit有効桁 約 7 桁
double64 bit有効桁 約 15 桁

キャストは「変換していい」とコンパイラに伝えるだけで、値が入るかどうかは確認されません。範囲を超えたら黙って壊れます。

// 安全に縮めたいときは検査付きの変換を使う
long big = 10_000_000_000L;
int safe = Math.toIntExact(big);   // ArithmeticException: integer overflow

// 桁あふれを検出する演算
int sum = Math.addExact(Integer.MAX_VALUE, 1);   // ArithmeticException

整数どうしの割り算

System.out.println(5 / 2);            // 2      小数部が捨てられる
System.out.println(5.0 / 2);          // 2.5    片方が double なら double
System.out.println((double) 5 / 2);   // 2.5    キャストしてから割る
System.out.println((double) (5 / 2)); // 2.0    ← 割ったあとでは手遅れ

int a = 7, b = 2;
System.out.println(a / b);            // 3
System.out.println((double) a / b);   // 3.5
System.out.println(a % b);            // 1      余り
System.out.println(-7 / 2);           // -3     0 方向に切り捨て
System.out.println(-7 % 2);           // -1     符号は左側に従う

System.out.println(1 / 0);            // ArithmeticException: / by zero
System.out.println(1.0 / 0);          // Infinity   浮動小数点は例外にならない

整数の 0 除算は例外、浮動小数点の 0 除算は Infinity と挙動が分かれます。

文字列との変換

// 文字列 → 数値
int n = Integer.parseInt("42");           // 42
int hex = Integer.parseInt("2A", 16);     // 42   基数を指定できる
long l = Long.parseLong("42");
double d = Double.parseDouble("1.5");

Integer boxed = Integer.valueOf("42");    // Integer(キャッシュが効く)

Integer.parseInt("42abc");                // NumberFormatException
Integer.parseInt("");                     // NumberFormatException
Integer.parseInt(null);                   // NumberFormatException
Integer.parseInt(" 42");                  // NumberFormatException(空白も不可)

// 数値 → 文字列
String s1 = String.valueOf(42);           // null でも "null" になり安全
String s2 = Integer.toString(42);
String s3 = "" + 42;                      // 動くが非推奨
String s4 = String.format("%,d", 1234567); // "1,234,567"

安全に変換する

public static int toIntOrDefault(String s, int defaultValue) {
    if (s == null) return defaultValue;
    try {
        return Integer.parseInt(s.trim());
    } catch (NumberFormatException e) {
        return defaultValue;
    }
}

// Optional で返す
public static OptionalInt toInt(String s) {
    try {
        return OptionalInt.of(Integer.parseInt(s));
    } catch (NumberFormatException | NullPointerException e) {
        return OptionalInt.empty();
    }
}

parseInt()前後の空白も許しません。フォームから受け取った値は trim() してから渡してください。

オートボクシング

Integer boxed = 42;          // 自動でボックス化(Integer.valueOf(42))
int unboxed = boxed;         // 自動でアンボックス化(boxed.intValue())

List<Integer> list = new ArrayList<>();
list.add(1);                 // int → Integer に自動変換

Map<String, Integer> map = new HashMap<>();
int count = map.get("missing");
// NullPointerException   ← null を int にできない

// 対処
int safe = map.getOrDefault("missing", 0);
Integer maybe = map.get("missing");
int safe2 = (maybe != null) ? maybe : 0;

Integernull のときアンボックス化で NPE」は Java で最も見つけにくいバグの 1 つです。行のどこにも . が無いのに NPE が出たら、これを疑ってください。

== で比較しない

Integer a = 100, b = 100;
System.out.println(a == b);        // true   -128〜127 はキャッシュされている

Integer c = 200, d = 200;
System.out.println(c == d);        // false  別のインスタンス
System.out.println(c.equals(d));   // true

int e = 200;
System.out.println(c == e);        // true   片方が int ならアンボックス化される

詳細は 比較演算子 を参照してください。ラッパー型の比較は必ず equals()Objects.equals() を使います。

参照型のキャスト

Object o = "文字列";

String s = (String) o;             // ダウンキャスト(実際の型が合っていれば OK)
// Integer i = (Integer) o;        // ClassCastException

// 先に確認する
if (o instanceof String) {
    String s2 = (String) o;
}

// Java 16 以降はまとめて書ける(パターンマッチング)
if (o instanceof String s3) {
    System.out.println(s3.length());
}

// switch でも使える(Java 21 以降)
String result = switch (o) {
    case Integer i -> "整数 " + i;
    case String str -> "文字列 " + str.length();
    default -> "その他";
};

アップキャスト(子 → 親)は常に安全、ダウンキャスト(親 → 子)は実行時に失敗し得ます。キャストの前に instanceof で確認してください。

金額の計算には BigDecimal

System.out.println(0.1 + 0.2);                 // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3);          // false

// double から作ると誤差ごと取り込む
new BigDecimal(0.1);                           // 0.1000000000000000055511...

// 文字列から作る
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));                  // 0.3

// 割り算は丸め方を必ず指定する
BigDecimal r = a.divide(b, 2, RoundingMode.HALF_UP);

// 比較は equals ではなく compareTo
new BigDecimal("1.0").equals(new BigDecimal("1.00"));      // false(スケールが違う)
new BigDecimal("1.0").compareTo(new BigDecimal("1.00"));   // 0(等しい)

BigDecimal.divide() で丸めを指定しないと、割り切れないときに ArithmeticException: Non-terminating decimal expansion になります。

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 型変換
  2. ジェネリクス

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