Javaにおけるプリミティブ型とオブジェクト型(ラッパークラス)の違いについてメモ。
また、検証では Java の REPL ツールである JShell を使用する。
$ jshell jshell> System.out.println("Hello"); Hello
そのため、変数の型を確認できる /v
という JShell 特有の記法も使用しているので注意。
jshell> /v 3 | Integer nullableInteger = null
※ 以後コードを書く際は jshell>
の表記を省略する
1. 値の比較
正直このメモを残すために記事を書いてる。
オブジェクト型の比較では equals()
メソッドを使用する必要がある。
int a = 5; int b = 5; System.out.println(a == b); // true, 値に基づく比較 String c = new String("Hello"); String d = new String("Hello"); Integer d = new Integer(5); System.out.println(c == d); // false, 参照が異なるため System.out.println(c.equals(d)); // true, 値に基づく比較
2. コレクションフレームワーク
Javaのコレクションフレームワークはオブジェクトを格納するために設計されている。
プリミティブ型をこれらのコレクションに直接格納することはできない。
var intList = List.of(1, 2, 3); // 自動的にプリミティブ型のintがIntegerになる /v intList // 出力: List<Integer> intList = [1, 2, 3]
3. Comparator
Arrays.sort
や Collections.sort
メソッドを使用する際、Comparator
を指定することで並び替えの基準をカスタマイズすることができる。
ただし、これらのメソッドの第一引数には、プリミティブ型の配列は指定できず、オブジェクト型の配列やコレクションを使用する必要がある。
Integer[] array = {3, 1, 4, 1, 5, 9, 2, 6, 5}; Arrays.sort(array, Comparator.reverseOrder()); System.out.println(Arrays.toString(array)); // [9, 6, 5, 5, 4, 3, 2, 1, 1]
上記のコードでは、Integer[]
(オブジェクト型の配列)を、Comparator.reverseOrder()
を用いて降順にソートしている。
一方で、プリミティブ型の配列(例えばint[]
)を用いる場合、Arrays.sort
メソッドに Comparator
を直接指定することはできない。
int[] primitiveArray = {3, 1, 4, 1, 5, 9, 2, 6, 5}; // Arrays.sort(primitiveArray, Comparator.reverseOrder()); // コンパイルエラー
これは、Comparator
がオブジェクト間の比較を行うためのインターフェースであり、プリミティブ型には適用できないため。
プリミティブ型の配列をカスタムの順序でソートするには、まず対応するラッパー型の配列に変換するか、手動でソートロジックを実装する必要がある。
4. ジェネリクス
ジェネリクスはオブジェクト型のみを扱う。
つまり、List<Integer>
はできるが List<int>
はできない。
List<Integer> validList = new ArrayList<>(); // List<int> invalidList = new ArrayList<>(); // コンパイルエラーになる
5. NULLの扱い
プリミティブ型は NULL を持つことができないが、オブジェクト型は NULL を持つことができる。
Integer nullableInteger = null; // int nullInt = null; // コンパイルエラーになる
6. ストリームAPI
Arrays.stream
の返り値の型に気をつける必要がある。
例えば、Stream<Integer>
と IntStream
の違い。
Stream<Integer>
:Integer
オブジェクトのストリームIntStream
: プリミティブ型int
の値のストリーム
Integer[] objectArray = {1, 2, 3, 4, 5}; Stream<Integer> objectStream = Arrays.stream(objectArray); /v objectStream // 出力: | Stream<Integer> objectStream = java.util.stream.ReferencePipeline$Head@300ffa5d
int[] primitiveArray = {1, 2, 3, 4, 5}; IntStream intStream = Arrays.stream(primitiveArray); /v intStream // 出力: | IntStream intStream = java.util.stream.IntPipeline$Head@4d405ef7
これらの違いにより、ストリームの操作方法に違いが生じる。
例えば、Stream<Integer>
には sum()
メソッドが定義されていないが、IntStream
には sum()
メソッドが存在する。
int[] primitiveArray = {1, 2, 3, 4, 5}; int sum = Arrays.stream(primitiveArray).sum(); System.out.println(sum); // 15 // Integer`オブジェクトの配列に対して同じ操作を行うと、エラーが発生 Integer[] objectArray = {1, 2, 3, 4, 5}; // int sumObject = Arrays.stream(objectArray).sum(); // コンパイルエラー
エラーの理由は、Arrays.stream(objectArray)
が Stream<Integer>
を生成し、この型のストリームには sum()
メソッドが定義されていないため。
この場合、mapToInt()
メソッドを使用して Stream<Integer>
を IntStream
に変換する必要がある。
Integer[] objectArray = {1, 2, 3, 4, 5}; int sumObject = Arrays.stream(objectArray) .mapToInt(Integer::intValue) .sum(); System.out.println(sumObject); // 15
このコードでは、mapToInt(Integer::intValue)
が各Integer
オブジェクトをプリミティブ型のint
に変換し、sum()
メソッドを適用している。