ぽこやかざん雑記

データエンジニア / 下町モルモット / 広島カープファン / 深夜の馬鹿力 / おくやま

Javaのプリミティブ型とオブジェクト型の違い

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.sortCollections.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()メソッドを適用している。