Javaの「可変長引数」をざっくりまとめてみた[Java初心者]



## はじめに

こんにちは。
プログラミング初心者Wakinozaと申します。
Java勉強中に調べたことを記事にまとめています。

十分気をつけて執筆していますが、なにぶん初心者が書いた記事なので、理解が浅い点などあるかと思います。
記事を参考にされる方は、初心者の記事であることを念頭において、お読みいただけると幸いです。
間違い等あれば、指摘いただけると助かります。

## 対象読者

- Javaを勉強中の方
- Java Silver試験を勉強中の方
- Javaの「可変長引数」についてざっくり知りたい方

## 目次

1\. 可変長引数とは
2\. 可変長引数のルール
3\. 可変長引数のオーバーロード
4\. メリットとデメリット

## 本文

### 1\. 可変長引数とは

多くのメソッドは開発時に引数の数が決まっているため、必要な数に応じて引数を設定します。
しかし時として、引数の数が定まっていないメソッドを作る必要が出てきます。
その際に使えるのが、「可変長引数」です。

「可変長引数」とは、同じデータ型の引数をいくつでも受け取れるシステムです。
受け取った引数は、内部で配列に変換してからメソッドに渡されます。
そのため、仮に引数なしでメソッドを呼び出しても、内部では「個数０個の配列」として扱われるため、エラーにはなりません。

メソッドの引数に「データ型...引数変数名」と記述することで、可変長引数を定義できます。
以下のコードをご覧ください。

```Java
public void doMethod(String...s){
  //any code
}
```

doMethodメソッド引数として、String型の可変長引数sを定義しています。

### 2\. 可変長引数のルール

可変長引数のルールは、３つあります

1, 可変長引数と異なるデータ型の引数を併記することができますが、可変長引数を必ず最後に置かなければなりません

```Java
public void doMethod(int n, String...s){} //OK
public void doMethod(String...s, int n){} //注意：エラー
```

2, 可変長引数は1つのメソッドに1つしか定義できません。複数定義するとエラーになります。

```Java
public void doMethod(String...s){}          //OK
public void doMethod(String...s, int...n){} //注意：エラー
``` 

3, 引数の数が明確な通常のメソッドと、可変長引数を利用したメソッドがオーバーロードしている場合、通常のメソッドが優先されます

```Java
//doMethod(100,200)を呼び出した場合
public void doMethod(int...n){} 
public void doMethod(int a, int b){}//こちらが優先して呼び出される
``` 

### 3\. 可変長引数のオーバーロード

可変長引数を利用したメソッドもオーバーロードが可能です。
ただし、先のルールでも説明したように、可変長引数を利用したメソッドは、利用しないメソッドより優先順位が下がります。
ここでは、オーバーロード時のメソッド呼び出しの優先順位に関わる複数の要素を、具体的なコード例と共に説明していきます。

以下のコードをご覧ください。

```Java
//doMethod(100)を呼び出すとする。
public void doMethod(int n){}      //①完全一致
public void doMethod(long n){}     //②暗黙の型変換
public void doMethod(Integer n){}  //③オートボクシング 
public void doMethod(int...n){}    //④可変長引数
``` 

まずは、４つのメソッドを１つずつ見ていきましょう。
①は引数にint型を受け取ります。
②の引数はlong型ですが、暗黙の型変換があるため、int型の引数も受け取ることができます。
③の引数はラッパークラスのInteger型ですが、オートボクシング（プリミティブ型とラッパークラス間の自動変換機能）があるため、int型の引数も受け取ることができます。
④は可変長引数を受け取るため、int型の引数を受け取ることができます。

つまり、この４つのメソッドは全てint型の引数を受け取れるということです。
この状態で、doMethod(100)を呼び出すとどうなるでしょうか。

実は、オーバーロードした複数のメソッドが同じデータ型を受け取れる場合、どのメソッドを呼び出すのか優先順位が決まっています。
優先順位は、以下の通りです。

1,完全一致
2,暗黙の型変換
3,オートボクシング
4,可変長引数

doMethod(100)を呼び出すと、優先順位が一番高い「完全一致」の①のメソッドが呼び出されます。

上の例では完全一致のメソッドが存在するため、どのメソッドが呼び出されるかは明確でした。
しかし、それ以外の優先順位が低い要素は見逃されがちです。
そのため、暗黙の型変換・オートボクシング・可変長引数によるオーバーロードを見逃すと、思わぬ挙動を引き起こす可能性があります。

メソッドをオーバーロードさせる際は、優先順位を意識して記述する必要があります。

### 4\. メリットとデメリット

最後に、可変長引数のメリットとデメリットを説明します。

メリットは主に３つあります。

- 可変長引数なら、同じデータ型の引数を複数受け取れます。そのため、引数の数を気にすることなく、メソッドを呼び出すことができます

- 同じデータ型の引数を複数受け取るメソッドを作る場合、引数を１つずつ定義するとメソッドの記述が長くなり、可読性も低下します。可変超引数にすると、複数の引数を簡潔に記述することができます

- 配列を受け取るメソッドを定義する場合、通常は配列を作成する記述が必要です。しかし、可変長引数を使えば、配列を作成する記述を省略し、直接データをメソッドに渡すことができます

デメリットは主に２つあります。

- オーバーロードしている場合、コンパイラがどのメソッドを呼び出すべきか判断できずエラーとなる可能性があります
- 可変長引数とジャネリクスを組み合わせた場合に、「ヒープ汚染」を起こす可能性があります。ヒープ汚染とは、オブジェクトが本来あるべき型とは異なる型付けをされてしまう状態です。コンパイラによるチェックをすり抜けて、実行時に予期しないエラーを起こす可能性があります


## まとめ

- 「可変長引数」とは、同じデータ型の引数をいくつでも受け取れるシステムです。受け取った引数は、内部で配列に変換してからメソッドに渡されます
- １つのメソッドに複数の可変長引数を定義することはできません。また、可変長引数を定義する場合は、必ず引数の最後に置く必要があります
- 引数の数が明確なメソッドと可変長引数の利用したメソッドがオーバーロードしている場合、引数の数が明確なメソッドが優先して呼び出されます

---------------

記事は以上です。
最後までお読みいただき、ありがとうございました。

##  参考情報一覧

この記事は以下の情報を参考にして執筆しました。

- [オラクル認定資格教科書 JavaプログラマSilverSE11]
- [パーフェクトJava 改訂3版]