これはJavaの変数宣言とオブジェクト生成という2つの異なる処理が組み合わさっているためです。
結論から言うと、型名を2回記載するのは、
- 左側:配列型の変数(参照)を宣言するため。
- 右側:実際にメモリ上に配列の実体(オブジェクト)を生成するため。
という、役割が異なるためです。
💡 役割の分離
ご提示のコードを分解してそれぞれの役割を見てみましょう。
型名 [] 配列名 = new 型名 [要素の数];
1. 左側: 変数宣言 (宣言部)
型名 [] 配列名
これは、配列名という名前の変数を定義しています。この変数は、指定された型名の要素を持つ配列オブジェクトを参照するための入れ物(参照型変数)であることを示しています。
- 例:
int [] numbers;→numbersはint型の要素を持つ配列への「参照」を保持する箱である、と宣言しています。
2. 右側: オブジェクト生成 (初期化部)
new 型名 [要素の数]
これは、実際にプログラムが実行時にヒープメモリ領域に配列オブジェクトの本体(実体)を生成(new)する部分です。ここで指定する型名は、配列の各要素が何のデータ型を持つのかをコンパイラと実行環境に伝えています。
- 例:
new int [5]→ メモリ上にint型の要素が5つ入る連続した領域を確保しなさい、という指示です。
🏢 別の例で考える
配列はオブジェクトなので、一般的なJavaのオブジェクト生成と同じ構造をしています。
Java
// 配列の宣言と生成
String [] sArray = new String [10];
// 一般的なオブジェクトの宣言と生成
Scanner sc = new Scanner(System.in);
この2つの例は構造が同じです。
| 部分 | 配列の例 | 一般的なオブジェクトの例 | 役割 |
| 左側 | String [] sArray | Scanner sc | 参照変数の型を宣言(この箱にどんなオブジェクトの参照を入れるか) |
| 右側 | new String [10] | new Scanner(System.in) | 実体オブジェクトをメモリに生成(どんな型のオブジェクトを作るか) |
このように、左側は変数の型(参照型)を決め、右側は新しく作るオブジェクトの実体の型を決めているため、同じ型名でも2回記載が必要になるのです。
✂️ 1回省略できるケース
なお、Javaには、配列を初期化子({...})を使って宣言と同時に初期化する特別な構文があり、この場合は右側の型名とnewを省略できます。
Java
int [] numbers = {10, 20, 30};
この場合、コンパイラが左側のint []から配列型であることと、中身の要素から要素数が3であることを推論してくれるため、簡潔に記述できます。

