JavaのintとIntegerの変換について(autoboxing/unboxing)
疑問
Javaで int や Integer のようなプリミティブとラッパークラスは意識して使い分けるべきか?
結論
意識して使い分けたほうが良い。
Autoboxing/Unboxingを使うのは、数値をコレクションに入れるなどの、インピーダンス・ミスマッチがある場合にのみ使用したほうが良い。
と、Oracleのサイトに書いてありました。
余談
Autoboxing/Unboxingがあるので、Integerで宣言してもintとして使えるし、intで宣言してもIntegerと同じようにCollectionに突っ込むことができます。
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String...args) { Integer i = 3; int j = 4; System.out.println("i + j = " + (i + j)); List<Integer> list = new ArrayList<>(); list.add(i); list.add(j); for (Integer x: list) { System.out.println(">> " + x); } } }
これをコンパイルして実行すると以下のような結果になります。
$ javac Test.java $ java Test i + j = 7 >> 3 >> 4
コンパイルに成功するし実行時エラーも出ません。
Integerで宣言してもintとして使えるし、intで宣言してもIntegerと同じようにCollectionに突っ込むことができます。(2回目)
コンパイル
以下のメソッドを逆コンパイルしてどういうコードが生成されているかを確認しました。
/** * int int -> int */ public int addIntIntToInt(int x, int y) { return x + y; } /** * Integer int -> int */ public int addIntegerIntoToInt(Integer x, int y) { return x + y; } /** * int Integer -> int */ public int addIntIntegerToInto(int x, Integer y) { return x + y; } /** * Integer Integer -> int */ public int addIntegerIntegerToInt(Integer x, Integer y) { return x + y; } /** * int int -> Integer */ public Integer addIntIntToInteger(int x, int y) { return x + y; } /** * Integer int -> Integer */ public Integer addIntegerIntoToInteger(Integer x, int y) { return x + y; } /** * int Integer -> Integer */ public Integer addIntIntegerToInteger(int x, Integer y) { return x + y; } /** * Integer Integer -> Integer */ public Integer addIntegerIntegerToInteger(Integer x, Integer y) { return x + y; }
int と int を受け取って int を返す
引数をスタックに積んで add してその値を返しています。
/** * int int -> int */ public int addIntIntToInt(int x, int y) { return x + y; }
public int addIntIntToInt(int, int); descriptor: (II)I flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: iload_1 1: iload_2 2: iadd 3: ireturn LineNumberTable: line 6: 0
Integer と int を受け取って int を返す
x + y
がコンパイルすると x.intValue() + y
になっているのがわかります。
/** * Integer int -> int */ public int addIntegerIntoToInt(Integer x, int y) { return x + y; }
public int addIntegerIntoToInt(java.lang.Integer, int); descriptor: (Ljava/lang/Integer;I)I flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: aload_1 1: invokevirtual #7 // Method java/lang/Integer.intValue:()I 4: iload_2 5: iadd 6: ireturn LineNumberTable: line 13: 0
int と Integer を受け取って int を返す
x + y
が x + y.intValue()
にコンパイルされてます。
/** * int Integer -> int */ public int addIntIntegerToInto(int x, Integer y) { return x + y; }
public int addIntIntegerToInto(int, java.lang.Integer); descriptor: (ILjava/lang/Integer;)I flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: iload_1 1: aload_2 2: invokevirtual #7 // Method java/lang/Integer.intValue:()I 5: iadd 6: ireturn LineNumberTable: line 20: 0
Integer と Integer を受け取って int を返す
x + y
が x.intValue() + y.intValue()
にコンパイルされています。
/** * Integer Integer -> int */ public int addIntegerIntegerToInt(Integer x, Integer y) { return x + y; }
public int addIntegerIntegerToInt(java.lang.Integer, java.lang.Integer); descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)I flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: aload_1 1: invokevirtual #7 // Method java/lang/Integer.intValue:()I 4: aload_2 5: invokevirtual #7 // Method java/lang/Integer.intValue:()I 8: iadd 9: ireturn LineNumberTable: line 27: 0
int と int を受け取って Integer を返す
x + y
の結果に対して Integer.valueOf()
を実行して Integer
にしているのがわかります。
Integer.sum
は使っていません。
/** * int int -> Integer */ public Integer addIntIntToInteger(int x, int y) { return x + y; }
public java.lang.Integer addIntIntToInteger(int, int); descriptor: (II)Ljava/lang/Integer; flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: iload_1 1: iload_2 2: iadd 3: invokestatic #13 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 6: areturn LineNumberTable: line 34: 0
Integer と int を受け取って Integer を返す
Integer
は int
にして計算したあと、 Integer.ValueOf()
で Integer
に変換しています。
/** * Integer int -> Integer */ public Integer addIntegerIntoToInteger(Integer x, int y) { return x + y; }
public java.lang.Integer addIntegerIntoToInteger(java.lang.Integer, int); descriptor: (Ljava/lang/Integer;I)Ljava/lang/Integer; flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: aload_1 1: invokevirtual #7 // Method java/lang/Integer.intValue:()I 4: iload_2 5: iadd 6: invokestatic #13 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 9: areturn LineNumberTable: line 41: 0
int と Integer を受け取って Integer を返す
こちらも同様です。
/** * int Integer -> Integer */ public Integer addIntIntegerToInteger(int x, Integer y) { return x + y; }
public java.lang.Integer addIntIntegerToInteger(int, java.lang.Integer); descriptor: (ILjava/lang/Integer;)Ljava/lang/Integer; flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: iload_1 1: aload_2 2: invokevirtual #7 // Method java/lang/Integer.intValue:()I 5: iadd 6: invokestatic #13 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 9: areturn LineNumberTable: line 48: 0
Integer と Integer を受け取って Integer を返す
これも同じく、 Integer
をわざわざ int
にして計算し、その結果を Integer
に変換しています。
/** * Integer Integer -> Integer */ public Integer addIntegerIntegerToInteger(Integer x, Integer y) { return x + y; }
public java.lang.Integer addIntegerIntegerToInteger(java.lang.Integer, java.lang.Integer); descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer; flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: aload_1 1: invokevirtual #7 // Method java/lang/Integer.intValue:()I 4: aload_2 5: invokevirtual #7 // Method java/lang/Integer.intValue:()I 8: iadd 9: invokestatic #13 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 12: areturn LineNumberTable: line 55: 0
性能試験
は面倒なのでやらないですが、コンパイラの出力を見ればループの中で Autoboxing/Unboxing を使うのはちょっとキモチワルイ感じがします。