Javaで文字列をintにするにはInteger.parseInt(文字列)、intを文字列にするにはString.valueOf(数値)を使います。 (int)のようなキャストは「数値型どうし」の変換に使うもので、文字列には使えません。ここを押さえるだけで、型変換で迷う場面の多くは解決します。

ただし、parseIntは前後の空白・空文字・null・小数・範囲外の値でNumberFormatExceptionを投げます。一方で、意外なことに全角数字の「12」は受け付けます。この記事では、こうした「やってみないとわからない」挙動を、すべてOpenJDK 21.0.10で実際に実行した出力つきで紹介します。String⇔int/long/double/Integer/BigDecimal/char/booleanの変換早見表、parseIntとvalueOfの違い(Integerキャッシュと==比較)、キャストの切り捨てとオーバーフロー、そのまま貼って使える安全な変換ユーティリティまでまとめました。

Java 型変換の早見表

文字列から各型へ(String → ○○)

変換したい型 メソッド 戻り値 変換できないとき
int Integer.parseInt(s) int NumberFormatException
Integer Integer.valueOf(s) Integer NumberFormatException
long Long.parseLong(s) long NumberFormatException
double Double.parseDouble(s) double NumberFormatException(nullはNullPointerException)
BigDecimal new BigDecimal(s) BigDecimal NumberFormatException
char s.charAt(0) char 空文字はStringIndexOutOfBoundsException
boolean Boolean.parseBoolean(s) boolean 例外なし。"true"(大文字小文字無視)以外はすべてfalse
16進・2進など Integer.parseInt(s, 16) int NumberFormatException
0x1Fの形 Integer.decode(s) Integer NumberFormatException

各型から文字列へ(○○ → String)

元の型 おすすめ ほかの書き方 ポイント
int / long / double String.valueOf(n) Integer.toString(n)、"" + n どれも結果は同じ
Integer(nullの可能性あり) String.valueOf(obj) Objects.toString(obj, "既定値") nullなら文字列"null"になる
BigDecimal bd.toPlainString() bd.toString() toString()は1E+3のような指数表記になることがある
char String.valueOf(c) Character.toString(c)
boolean String.valueOf(b) Boolean.toString(b)
ゼロ埋め・桁区切り String.format("%05d", n) "%,d".formatted(n) 表示用の整形

数値型どうし(キャストと変換)

やりたいこと 書き方 結果の例(実行済み)
double → int(切り捨て) (int) 3.99 3(負の数は(int) -3.99 → -3)
double → int(四捨五入) (int) Math.round(3.99) 4
long → int(安全に) Math.toIntExact(l) 範囲外ならArithmeticException
int → long / double そのまま代入(拡大変換) long l = i;
char → 数字の値 c - '0'、Character.getNumericValue(c) '7' → 7
int → char (char) 65 A

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Java int⇔String変換|parseIntとキャストの違い・例外対策」

動作確認した環境

この記事のコードは、すべてOpenJDK 21.0.10でjava -Dstdout.encoding=UTF-8 ファイル名.java(単一ファイル実行、外部ライブラリなし)として実行し、出力をそのまま載せています。 -Dstdout.encoding=UTF-8は全角文字を文字化けさせずに表示するための指定です。Java 22〜25の機能はこの環境で動かせないため、公式ドキュメント(JEP)で確認した内容として注記しています。

String ⇔ 各型の変換をまとめて動かす

まずは早見表のメソッドを一度に動かして、戻り値を確認しましょう。 そのままConvertTable.javaとして保存し、java ConvertTable.javaで実行できます。

import java.math.BigDecimal;

public class ConvertTable {
    public static void main(String[] args) {
        // String → 各型
        int i = Integer.parseInt("123");
        Integer boxed = Integer.valueOf("123");
        long l = Long.parseLong("9000000000");
        double d = Double.parseDouble("3.14");
        BigDecimal bd = new BigDecimal("0.10");
        char c = "A".charAt(0);
        boolean b1 = Boolean.parseBoolean("TRUE");
        boolean b2 = Boolean.parseBoolean("yes");
        System.out.println("parseInt      : " + i);
        System.out.println("valueOf       : " + boxed);
        System.out.println("parseLong     : " + l);
        System.out.println("parseDouble   : " + d);
        System.out.println("BigDecimal    : " + bd);
        System.out.println("charAt(0)     : " + c);
        System.out.println("parseBoolean TRUE: " + b1 + " / yes: " + b2);

        // 各型 → String
        System.out.println("String.valueOf(int)    : " + String.valueOf(123));
        System.out.println("BigDecimal.toPlainString: " + new BigDecimal("1E+3").toPlainString());
        Integer nullInt = null;
        System.out.println("String.valueOf(null Integer): " + String.valueOf(nullInt));
        System.out.println("String.format(%05d)    : " + String.format("%05d", 42));
        System.out.println("Integer.toString(255,16): " + Integer.toString(255, 16));
        System.out.println("parseInt(\"ff\",16)      : " + Integer.parseInt("ff", 16));

        // char ⇔ int
        char ch = '7';
        System.out.println("(int)'7'               : " + (int) ch);
        System.out.println("'7' - '0'              : " + (ch - '0'));
        System.out.println("Character.getNumericValue('7'): " + Character.getNumericValue(ch));
        System.out.println("(char)65               : " + (char) 65);
    }
}

実行結果(OpenJDK 21.0.10):

parseInt      : 123
valueOf       : 123
parseLong     : 9000000000
parseDouble   : 3.14
BigDecimal    : 0.10
charAt(0)     : A
parseBoolean TRUE: true / yes: false
String.valueOf(int)    : 123
BigDecimal.toPlainString: 1000
String.valueOf(null Integer): null
String.format(%05d)    : 00042
Integer.toString(255,16): ff
parseInt("ff",16)      : 255
(int)'7'               : 55
'7' - '0'              : 7
Character.getNumericValue('7'): 7
(char)65               : A

出力から読み取れるポイント

  • Boolean.parseBoolean("yes")はfalseです。例外にはならず、"true"以外はすべてfalseになります。"yes"や"1"を真として扱いたいときは、自分で比較する処理を書きます。
  • (int)'7'は55です。これは数字の値ではなく、文字'7'の文字コード(Unicodeのコードポイント)です。数字としての値がほしいときは'7' - '0'かCharacter.getNumericValue()を使います。
  • BigDecimalのtoString()はnew BigDecimal("1E+3")で1E+3のような指数表記になります。 画面やCSVに出すときはtoPlainString()(結果は1000)が扱いやすいです。
  • nullのIntegerをString.valueOf()に渡すと文字列"null"になります。表示用ならObjects.toString(obj, "")で既定値を決めておくと安心です。

int→StringはString.valueOf()・Integer.toString()・"" + nのどれでも結果は同じなので、読みやすさで選べば十分です。チームで統一するなら、long・double・Objectにも同じ書き方で使えるString.valueOf()がおすすめです。

parseInt と valueOf の違い

Integer.parseInt()はプリミティブのintを、Integer.valueOf()はラッパークラスのIntegerを返します。 数値として計算するだけならparseInt、List<Integer>に入れる・nullを表したいなどオブジェクトが必要なときはvalueOfを使います。

比較項目 Integer.parseInt(s) Integer.valueOf(s)
戻り値 int(プリミティブ) Integer(オブジェクト、nullも持てる)
キャッシュ 関係なし -128〜127は同じオブジェクトを返す
変換できないとき NumberFormatException NumberFormatException
向いている使い方 計算・比較・配列の添字 コレクション、ジェネリクス

キャッシュの範囲で == の結果が変わる

Integerどうしを==で比べると、値が同じでもfalseになることがあります。 Integer.valueOf()は-128〜127の値について、あらかじめ作っておいた同じオブジェクトを返します(Javadocに明記されている仕様です)。範囲外の値は新しいオブジェクトになるため、==(同じオブジェクトかどうか)ではfalseになります。

public class ValueOfCache {
    public static void main(String[] args) {
        int a = Integer.parseInt("127");          // int(プリミティブ)
        Integer b = Integer.valueOf("127");       // Integer(ラッパー)
        System.out.println("parseInt の型: int / valueOf の型: " + b.getClass().getName());

        Integer x127 = Integer.valueOf("127"), y127 = Integer.valueOf("127");
        Integer x128 = Integer.valueOf("128"), y128 = Integer.valueOf("128");
        Integer xm128 = Integer.valueOf(-128), ym128 = Integer.valueOf(-128);
        Integer xm129 = Integer.valueOf(-129), ym129 = Integer.valueOf(-129);
        System.out.println("127  == : " + (x127 == y127));
        System.out.println("128  == : " + (x128 == y128));
        System.out.println("-128 == : " + (xm128 == ym128));
        System.out.println("-129 == : " + (xm129 == ym129));
        System.out.println("128 equals : " + x128.equals(y128));
        int prim = 128;
        System.out.println("Integer(128) == int 128 : " + (x128 == prim));

        Integer autoA = 1000, autoB = 1000;     // オートボクシングも valueOf を使う
        System.out.println("auto 1000 == : " + (autoA == autoB));

        Integer nothing = null;
        try {
            int n = nothing;                      // アンボクシングで NPE
        } catch (NullPointerException e) {
            System.out.println("null を int に代入: " + e.getMessage());
        }
    }
}

実行結果(OpenJDK 21.0.10):

parseInt の型: int / valueOf の型: java.lang.Integer
127  == : true
128  == : false
-128 == : true
-129 == : false
128 equals : true
Integer(128) == int 128 : true
auto 1000 == : false
null を int に代入: Cannot invoke "java.lang.Integer.intValue()" because "<local14>" is null

127まではtrue、128からはfalseになりました。テストで小さい値しか使っていないと気づきにくいポイントです。

  • Integerどうしの比較はequals()を使う。片方がintなら==でも値の比較になる(アンボクシング)
  • Integer a = 1000;のような代入(オートボクシング)も内部でvalueOfを使うため、同じ注意が必要
  • nullのIntegerをintに代入するとNullPointerException。メッセージの<local14>は、デバッグ情報なしでコンパイルされたときのローカル変数の表記です(javac -gでコンパイルすると変数名が表示されます)

キャッシュの上限は、JVMオプション-XX:AutoBoxCacheMaxで広げられることがJDKのソースコードのコメントに記載されています。ただし上限に頼らずequals()で比べるのが確実です。文字列の==とequalsの違いは「Javaの文字列比較」の記事で詳しく扱っています。

NumberFormatException が起きる入力・起きない入力

parseIntは「符号つきの10進数字だけ」を受け付けます。 空白や小数点、カンマが1文字でも入ると例外になります。どの入力が通るのか、実際に試しました。

public class ParseErrors {
    static void tryParse(String label, String s) {
        try {
            int v = Integer.parseInt(s);
            System.out.println(label + " -> OK: " + v);
        } catch (NumberFormatException e) {
            System.out.println(label + " -> " + e.getClass().getSimpleName() + ": " + e.getMessage());
        }
    }
    public static void main(String[] args) {
        tryParse("\"12\"        ", "12");
        tryParse("\"12\"(全角) ", "12");
        tryParse("\"٣\"(アラビア数字)", "٣");
        tryParse("\" 12\"       ", " 12");
        tryParse("null        ", null);
        tryParse("\"\"          ", "");
        tryParse("\"2147483648\"", "2147483648");
        tryParse("\"1.5\"       ", "1.5");
        tryParse("\"+12\"       ", "+12");
        tryParse("\"1,000\"     ", "1,000");
        tryParse("\"0x1F\"      ", "0x1F");
        tryParse("\"-12\"(全角マイナス)", "-12");
        System.out.println("Integer.decode(\"0x1F\") -> " + Integer.decode("0x1F"));
        System.out.println("Double.parseDouble(\" 1.5 \") -> " + Double.parseDouble(" 1.5 "));
        System.out.println("Double.parseDouble(\"1.5\") -> " + tryDouble("1.5"));
        System.out.println("new BigDecimal(\"12\") -> " + new java.math.BigDecimal("12"));
        try { Double.parseDouble(null); } catch (Exception e) { System.out.println("Double.parseDouble(null) -> " + e.getClass().getSimpleName() + ": " + e.getMessage()); }
        try { new java.math.BigDecimal(" 12"); } catch (Exception e) { System.out.println("new BigDecimal(\" 12\") -> " + e.getClass().getSimpleName() + ": " + e.getMessage()); }
    }
    static String tryDouble(String s) {
        try { return String.valueOf(Double.parseDouble(s)); }
        catch (NumberFormatException e) { return "NumberFormatException: " + e.getMessage(); }
    }
}

実行結果(OpenJDK 21.0.10):

"12"         -> OK: 12
"12"(全角)  -> OK: 12
"٣"(アラビア数字) -> OK: 3
" 12"        -> NumberFormatException: For input string: " 12"
null         -> NumberFormatException: Cannot parse null string
""           -> NumberFormatException: For input string: ""
"2147483648" -> NumberFormatException: For input string: "2147483648"
"1.5"        -> NumberFormatException: For input string: "1.5"
"+12"        -> OK: 12
"1,000"      -> NumberFormatException: For input string: "1,000"
"0x1F"       -> NumberFormatException: For input string: "0x1F"
"-12"(全角マイナス) -> NumberFormatException: For input string: "-12"
Integer.decode("0x1F") -> 31
Double.parseDouble(" 1.5 ") -> 1.5
Double.parseDouble("1.5") -> NumberFormatException: For input string: "1.5"
new BigDecimal("12") -> 12
Double.parseDouble(null) -> NullPointerException: Cannot invoke "String.trim()" because "in" is null
new BigDecimal(" 12") -> NumberFormatException: Character   is neither a decimal digit number, decimal point, nor "e" notation exponential mark.

入力別の結果と直し方

入力 parseIntの結果 理由 直し方
"12"(全角数字) OK:12 Character.digit()で判定するため、Unicodeの数字はすべて数字として扱われる 半角だけにしたいなら正規表現で事前チェック
"-12"(全角マイナス) 例外 符号として認められるのは半角の+と-だけ NormalizerでNFKC正規化してから変換
" 12" 例外 空白は数字ではない s.strip()で前後の空白を除く
null 例外(Cannot parse null string) NumberFormatExceptionになる(NPEではない) 事前にnullチェック
"" 例外 数字が1文字もない isBlank()で事前チェック
"2147483648" 例外 intの最大値(2147483647)を超えた Long.parseLong()を使う
"1.5" 例外 小数点は受け付けない Double.parseDouble()かBigDecimal
"1,000" 例外 カンマは受け付けない replace(",", "")してから変換
"0x1F" 例外 接頭辞は解釈しない Integer.decode()かparseInt("1F", 16)
"+12" OK 先頭の符号は1文字まで受け付ける ―

意外なのは、Integer.parseInt("12")が12になることです。 Javadocでも「文字はすべて10進数の数字でなければならない」とされており、その判定にCharacter.digit()が使われるため、全角数字やアラビア・インド数字(٣)も数字として受け付けます。逆に、全角の「-」や「,」は数字ではないので例外になります。

Double.parseDouble は前後の空白を許す

同じ「文字列→数値」でも、Double.parseDouble()は前後の空白を取り除いてから解釈します(出力の" 1.5 " → 1.5)。一方で全角数字の"1.5"は例外でした。さらにnullを渡したときは、parseIntがNumberFormatExceptionなのに対して、parseDoubleとnew BigDecimalはNullPointerExceptionになります(BigDecimalも別途実行して確認)。

入力 Integer.parseInt Double.parseDouble new BigDecimal
前後の空白 " 12" 例外 OK(空白を除いて解釈) 例外
全角数字 "12" OK 例外 OK
null NumberFormatException NullPointerException NullPointerException

メソッドごとに受け付ける範囲が違うため、入力を受け取る側で「前後の空白を除く」「nullと空を先に弾く」をそろえておくと、どのメソッドを使っても同じ結果になります。

例外をそのまま投げたときのエラー全文

try-catchをしないと、プログラムは次のように止まります。For input string:の後ろの"..."に実際の入力がそのまま出るので、空白や全角文字が混ざっていないかをここで確認できます。

public class Uncaught {
    public static void main(String[] args) {
        String input = "12 ";
        int n = Integer.parseInt(input);
        System.out.println(n);
    }
}

実行結果(OpenJDK 21.0.10、標準エラー出力):

Exception in thread "main" java.lang.NumberFormatException: For input string: "12 "
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
    at java.base/java.lang.Integer.parseInt(Integer.java:662)
    at java.base/java.lang.Integer.parseInt(Integer.java:778)
    at Uncaught.main(Uncaught.java:4)

全角数字自体は通りますが、末尾の半角空白1文字で例外になりました。

全角を半角にそろえる・半角だけを許可する

入力を「半角数字だけ」に限定したいか、「全角も受け付けて半角に直したい」かで処理を選びます。

import java.text.Normalizer;
import java.util.regex.Pattern;

public class Ascii {
    private static final Pattern ASCII_INT = Pattern.compile("[+-]?[0-9]{1,10}");
    public static void main(String[] args) {
        for (String s : new String[]{"12", "12", "-12", "1,200"}) {
            String nfkc = Normalizer.normalize(s, Normalizer.Form.NFKC);
            System.out.println(s + " : ASCIIのみ=" + ASCII_INT.matcher(s).matches()
                + " / NFKC後=\"" + nfkc + "\"");
        }
        System.out.println("parseInt(NFKC(\"-12\")) = " + Integer.parseInt(Normalizer.normalize("-12", Normalizer.Form.NFKC)));
    }
}

実行結果(OpenJDK 21.0.10):

12 : ASCIIのみ=true / NFKC後="12"
12 : ASCIIのみ=false / NFKC後="12"
-12 : ASCIIのみ=false / NFKC後="-12"
1,200 : ASCIIのみ=false / NFKC後="1,200"
parseInt(NFKC("-12")) = -12

APIのパラメータやIDのように半角だけを許可したいなら正規表現で事前チェック、人が入力するフォームならNormalizerでNFKC正規化してから変換、と使い分けます。

正規表現の[0-9]は半角数字だけに一致します。\dも既定では半角数字のみ("12"にfalse)ですが、Pattern.UNICODE_CHARACTER_CLASSを付けると全角数字にも一致する(true)ことを確認しました。このように設定で意味が変わるため、意図をはっきりさせたいときは[0-9]と書くのがおすすめです。

キャストの使い方と、値が変わるパターン

キャスト((int)のような書き方)は、数値型どうしで「型を明示して変換する」ための書き方です。 小さい型から大きい型へは自動で変換されますが、大きい型から小さい型へは、情報が欠ける可能性があるのでキャストが必要です。

文字列はキャストできない(コンパイルエラー)

まず、よくある書き間違いを確認します。文字列と数値の間はキャストでは変換できず、コンパイルの時点でエラーになります。

public class CompileErrors {
    public static void main(String[] args) {
        int a = "123";
        int b = (int) "123";
        int c = 3.5;
        long big = 10L;
        int d = big;
        String s = 123;
    }
}

javac -d ce ce/CompileErrors.javaの結果(OpenJDK 21.0.10、英語表示):

ce/CompileErrors.java:3: error: incompatible types: String cannot be converted to int
        int a = "123";
                ^
ce/CompileErrors.java:4: error: incompatible types: String cannot be converted to int
        int b = (int) "123";
                      ^
ce/CompileErrors.java:5: error: incompatible types: possible lossy conversion from double to int
        int c = 3.5;
                ^
ce/CompileErrors.java:7: error: incompatible types: possible lossy conversion from long to int
        int d = big;
                ^
ce/CompileErrors.java:8: error: incompatible types: int cannot be converted to String
        String s = 123;
                   ^
5 errors

日本語ロケールで実行すると、エラー: 不適合な型: Stringをintに変換できません:、エラー: 不適合な型: 精度が失われる可能性があるdoubleからintへの変換のように表示されました(同じJDKで確認)。

エラー文 意味 直し方
String cannot be converted to int 文字列は数値に代入・キャストできない Integer.parseInt(s)
int cannot be converted to String 数値は文字列に代入できない String.valueOf(n)
possible lossy conversion from double to int 小数部が失われる可能性がある (int)でキャスト、またはMath.round()
possible lossy conversion from long to int 範囲外の値が入る可能性がある Math.toIntExact(l)、または(int)でキャスト

また、Object型の変数に入った文字列を(Integer)でキャストすると、コンパイルは通っても実行時にClassCastExceptionになります(下の実行結果の最終行)。文字列から数値への変換は、いつでもparseInt系のメソッドと覚えておくと迷いません。

縮小変換・オーバーフローを実行で確かめる

public class Casts {
    public static void main(String[] args) {
        System.out.println("(int) 3.99   = " + (int) 3.99);
        System.out.println("(int) -3.99  = " + (int) -3.99);
        System.out.println("(int) 1e10   = " + (int) 1e10);
        System.out.println("(int) Double.NaN = " + (int) Double.NaN);
        long big = 3_000_000_000L;
        System.out.println("(int) 3000000000L = " + (int) big);
        System.out.println("(byte) 200   = " + (byte) 200);
        try {
            int safe = Math.toIntExact(big);
        } catch (ArithmeticException e) {
            System.out.println("Math.toIntExact(3000000000L) -> ArithmeticException: " + e.getMessage());
        }
        int max = Integer.MAX_VALUE;
        System.out.println("MAX_VALUE + 1 = " + (max + 1));
        try { Math.addExact(max, 1); } catch (ArithmeticException e) { System.out.println("Math.addExact(MAX,1) -> " + e.getMessage()); }
        int x = 100000;
        System.out.println("int * int (100000*100000)  = " + (x * x));
        System.out.println("(long) x * x               = " + ((long) x * x));
        System.out.println("(long) (x * x)             = " + (long) (x * x));

        // 拡大変換(暗黙)
        byte bt = 10; short sh = bt; int in = sh; long lg = in; float fl = lg; double db = fl;
        System.out.println("byte→short→int→long→float→double: " + db);
        int precise = 123_456_789;
        float f = precise;
        System.out.println("int 123456789 → float : " + f + " → int に戻す: " + (int) f);
        long bigL = 9_007_199_254_740_993L;
        double dd = bigL;
        System.out.println("long 9007199254740993 → double → long : " + (long) dd);

        // 複合代入は暗黙のキャストが入る
        int total = 10;
        total += 2.7;
        System.out.println("int total = 10; total += 2.7 → " + total);
        // String はキャストできない
        Object o = "123";
        try {
            Integer bad = (Integer) o;
        } catch (ClassCastException e) {
            System.out.println("ClassCastException: " + e.getMessage());
        }
    }
}

実行結果(OpenJDK 21.0.10):

(int) 3.99   = 3
(int) -3.99  = -3
(int) 1e10   = 2147483647
(int) Double.NaN = 0
(int) 3000000000L = -1294967296
(byte) 200   = -56
Math.toIntExact(3000000000L) -> ArithmeticException: integer overflow
MAX_VALUE + 1 = -2147483648
Math.addExact(MAX,1) -> integer overflow
int * int (100000*100000)  = 1410065408
(long) x * x               = 10000000000
(long) (x * x)             = 1410065408
byte→short→int→long→float→double: 10.0
int 123456789 → float : 1.2345679E8 → int に戻す: 123456792
long 9007199254740993 → double → long : 9007199254740992
int total = 10; total += 2.7 → 12
ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')

出力から読み取れるポイント

① (int)は「0に向かって切り捨て」です。(int) 3.99は3、(int) -3.99は-3でした。負の数で「小さい方へ切り下げ」たいときはMath.floor()、四捨五入ならMath.round()を使います。下の表でMath.round(-3.5)が-3になるのは、Math.roundが「ちょうど0.5のときは大きい方へ丸める」仕様だからです。

やりたい丸め 書き方 3.99 -3.99 2.5 -3.5
0に向かって切り捨て (int) d 3 -3 2 -3
小さい方へ(切り下げ) (int) Math.floor(d) 3 -4 2 -4
大きい方へ(切り上げ) (int) Math.ceil(d) 4 -3 3 -3
四捨五入(0.5は大きい方へ) (int) Math.round(d) 4 -4 3 -3

※表の16個の値は、すべてOpenJDK 21.0.10で(int) d・(int) Math.floor(d)・(int) Math.ceil(d)・(int) Math.round(d)を実行して確認しました。

② double→intで範囲外は「最大値・最小値に張り付く」、NaNは0になります((int) 1e10は2147483647)。これはJava言語仕様(JLS 5.1.3)で決まっている動きです。

③ long→intで範囲外は「下位32ビットだけが残る」ため、30億が-1294967296という無関係に見える値になりました。(byte) 200が-56になるのも同じ理由です。範囲外を検出したいときはMath.toIntExact()を使うと、ArithmeticException: integer overflowで知らせてくれます。

④ intの計算結果はintのままです。100000 * 100000はintの範囲を超えて1410065408になりました。(long) (x * x)のように計算後にキャストしても遅く、(long) x * xのように計算前に片方をlongにするのがポイントです。Math.addExactやMath.multiplyExactを使えば、あふれたときに例外で検出できます。

⑤ 複合代入(+=など)には暗黙のキャストが入ります。 int total = 10; total += 2.7;はコンパイルエラーにならず、結果は12です。total = total + 2.7;と書くとエラーになるので、この違いは覚えておくと役立ちます。

拡大変換(自動で行われる変換)の一覧

小さい型から大きい型への代入は、キャストを書かなくても自動で行われます(拡大変換)。

元の型 自動で変換できる先 精度が落ちる可能性
byte short, int, long, float, double ―
short int, long, float, double ―
char int, long, float, double ―
int long, float, double float(有効桁 約7桁)
long float, double float、double(double は有効桁 約15〜17桁)
float double ―

int→floatやlong→doubleは「拡大変換」なのに、値が変わることがあります。 実行結果でも、123456789をfloatにして戻すと123456792に、9007199254740993をdoubleにして戻すと9007199254740992になりました。大きな整数を正確に扱いたいときは、longのまま計算するかBigDecimalを使います。

コピペで使える安全な変換ユーティリティ

入力値を数値にする処理は、「変換できなかったときにどうするか」を決めて1か所にまとめると、呼び出し側がすっきりします。ここでは、よく使う4パターンをまとめました。

parseIntOpt(変換できなければOptionalInt.empty())、parseIntOrDefault(既定値を返す)、parseIntInRange(範囲外もempty)、parseIntLenient("1,200"や"1.0"も受け付ける)の4つです。

import java.math.BigDecimal;
import java.util.Optional;
import java.util.OptionalInt;

public class SafeConvertDemo {
    public static void main(String[] args) {
        String[] inputs = {"42", " 42 ", "12", "", null, "abc", "2147483648", "1.5", "1,200", "1.0", "-7"};
        System.out.printf("%-14s %-16s %-10s %-12s %s%n", "入力", "parseIntOpt", "既定値0", "範囲1〜99", "lenient");
        for (String in : inputs) {
            String label = in == null ? "null" : "\"" + in + "\"";
            System.out.printf("%-14s %-16s %-10d %-12s %s%n",
                label,
                NumberParsers.parseIntOpt(in),
                NumberParsers.parseIntOrDefault(in, 0),
                NumberParsers.parseIntInRange(in, 1, 99),
                NumberParsers.parseIntLenient(in));
        }
    }
}

final class NumberParsers {
    private NumberParsers() {}

    /** 数字以外・範囲外・null・空なら OptionalInt.empty() を返す */
    static OptionalInt parseIntOpt(String s) {
        if (s == null) return OptionalInt.empty();
        String t = s.strip();
        if (t.isEmpty()) return OptionalInt.empty();
        try {
            return OptionalInt.of(Integer.parseInt(t));
        } catch (NumberFormatException e) {
            return OptionalInt.empty();
        }
    }

    /** 変換できなければ既定値を返す */
    static int parseIntOrDefault(String s, int defaultValue) {
        return parseIntOpt(s).orElse(defaultValue);
    }

    /** 範囲チェックつき(例:数量 1〜99) */
    static OptionalInt parseIntInRange(String s, int min, int max) {
        OptionalInt v = parseIntOpt(s);
        if (v.isPresent() && v.getAsInt() >= min && v.getAsInt() <= max) return v;
        return OptionalInt.empty();
    }

    /** "1,200" や "1.0" も受け付けて、整数として正確に表せるときだけ int にする */
    static Optional<Integer> parseIntLenient(String s) {
        if (s == null || s.isBlank()) return Optional.empty();
        try {
            BigDecimal bd = new BigDecimal(s.strip().replace(",", ""));
            return Optional.of(bd.intValueExact());   // 小数部あり・範囲外は ArithmeticException
        } catch (NumberFormatException | ArithmeticException e) {
            return Optional.empty();
        }
    }
}

実行結果(OpenJDK 21.0.10):

入力             parseIntOpt      既定値0       範囲1〜99       lenient
"42"           OptionalInt[42]  42         OptionalInt[42] Optional[42]
" 42 "         OptionalInt[42]  42         OptionalInt[42] Optional[42]
"12"           OptionalInt[12]  12         OptionalInt[12] Optional[12]
""             OptionalInt.empty 0          OptionalInt.empty Optional.empty
null           OptionalInt.empty 0          OptionalInt.empty Optional.empty
"abc"          OptionalInt.empty 0          OptionalInt.empty Optional.empty
"2147483648"   OptionalInt.empty 0          OptionalInt.empty Optional.empty
"1.5"          OptionalInt.empty 0          OptionalInt.empty Optional.empty
"1,200"        OptionalInt.empty 0          OptionalInt.empty Optional[1200]
"1.0"          OptionalInt.empty 0          OptionalInt.empty Optional[1]
"-7"           OptionalInt[-7]  -7         OptionalInt.empty Optional[-7]

ユーティリティの設計ポイント

  • strip()で前後の空白を除いてから変換しているので、" 42 "も42になります。trim()と違い、strip()は全角スペースなどUnicodeの空白も除きます(Java 11以降。前後に全角スペースを付けた「 12 」で、strip()は全角スペースを除き、trim()は残すことを確認)。
  • OptionalIntを返すと、「0が入力された」と「変換できなかった」を区別できます。 既定値方式は手軽ですが、0が正しい入力になりうる場面では区別がつかなくなります。
  • parseIntLenientはBigDecimal.intValueExact()で「小数部がなく、intに収まる」ときだけ値を返します。 "1.5"は小数部があるのでempty、"1.0"は1として扱われました。
  • 設定ファイルの読み込みのように「変換できない=設定ミス」の場合は、Optionalにせず入力値を含めたメッセージで例外を投げ直す方が原因を見つけやすくなります。
  • 単一ファイル実行では最初のクラスにmainが必要で、ユーティリティを先に書くとerror: can't find main(String[]) method in class: NumberParsersになりました(Java 21)。通常は別ファイルに分けて使います。

Optionalの使い方そのもの(orElseとorElseGetの違いなど)は、「Java Optionalの使い方」の記事で詳しく扱っています。

判断表:どの変換方法を選ぶか

やりたいこと 選ぶ方法 理由
コレクションに入れる・nullを扱う Integer.valueOf(s) Integerが返る
21億を超える整数 Long.parseLong(s) intの上限は2147483647
小数を含む文字列 Double.parseDouble(s) / new BigDecimal(s) 誤差を避けたいならBigDecimal
人が入力した値 strip() → 正規化 → parseInt → 範囲チェック 空白・全角・範囲外を順に処理
doubleを整数に(切り捨て) (int) d 0に向かって切り捨て
doubleを整数に(四捨五入) (int) Math.round(d) Math.roundはlongを返すのでキャスト
longをintに(範囲外は検出) Math.toIntExact(l) 範囲外でArithmeticException

Java 22〜25 で関係する変更(公式で確認)

この記事の検証環境はJava 21のため、以下は実行しておらず、OpenJDKの公式ページ(JEP)で確認した内容です。

バージョン 内容 状態
Java 23・24・25 JEP 455 / 488 / 507「パターン、instanceof、switchでのプリミティブ型」 Java 25ではJEP 507として3回目のプレビュー(--enable-previewが必要)
Java 25 JEP 512「コンパクトなソースファイルとインスタンスmainメソッド」 正式機能。IO.println()などで短く書けるようになった

JEP 507では、if (i instanceof byte b) { ... }のように書くと、iが情報を失わずにbyteに変換できるときだけ一致します。JEPの例ではint i = 1000のとき一致しません。キャストのように黙って値が変わるのではなく、範囲内かどうかを確かめてから変換できる書き方です。プレビュー機能のため、正式化されるまでは仕様が変わる可能性があります。Java 21〜25の正式機能の範囲では、この記事のMath.toIntExact()や範囲チェックの方法がそのまま使えます。

よくある質問

Q. Integer.parseInt()とInteger.valueOf()はどちらを使えばよいですか? A. 計算に使うならparseInt()、Integerオブジェクトが必要ならvalueOf()です。性能差はほとんど気にしなくてよい範囲ですが、valueOf()の戻り値を==で比べると、-128〜127の範囲外でfalseになる点だけ注意してください。

Q. 全角数字の「123」はparseIntで変換できますか? A. できます(Java 21で確認:結果は123)。 Unicodeの数字として扱われるためです。ただし全角のマイナス「-」やカンマ「,」は例外になります。半角だけを許可したい場合は正規表現[+-]?[0-9]+で事前にチェックし、全角も受け付けたい場合はNormalizerでNFKC正規化してから変換します。

Q. NumberFormatExceptionはtry-catchで囲むべきですか? A. 外から来る値(画面入力、ファイル、APIのパラメータ)を変換するときは囲むか、事前にチェックします。 プログラム内の定数など、必ず数字であるとわかっている値まで囲む必要はありません。この記事のNumberParsersのように、変換処理を1か所にまとめておくと、呼び出し側にtry-catchを散らばらせずに済みます。

まとめ

  • 文字列→intはInteger.parseInt()、int→文字列はString.valueOf()。キャストは数値型どうし専用
  • parseIntはint、valueOfはIntegerを返す。Integerどうしの比較はequals()(127までと128からで==の結果が変わる)
  • parseIntは空白・空文字・null・小数・カンマ・範囲外で例外。全角数字「12」は受け付ける
  • (int)は0に向かって切り捨て。四捨五入はMath.round()、範囲外の検出はMath.toIntExact()
  • int同士の大きな掛け算は計算前に(long)にする
  • 入力値の変換はstrip()→チェック→変換→範囲確認をユーティリティにまとめると扱いやすい

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Java int⇔String変換|parseIntとキャストの違い・例外対策」

株式会社RJCでは、Javaの型変換のような基礎の挙動を、実際に動かして確かめながら仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?