- JAVA
Java int⇔String変換|parseIntとキャストの違い・例外対策
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期連続の黒字決算。