LoginSignup
11
12

More than 5 years have passed since last update.

【Java】可変長引数のパラメータを配列にするときの注意

Posted at

リフレクションとか、JMockitのDeencapsulationとかをいじってるときに気をつけてないと結構はまるポイントになる気がするのでまとめました。

とりあえずいろんなパターンで動かす

Main.java

public class Main {

    public static void main(String[] args) {

        // 1
        test(1, "a", "b", "c");

        // 2
        test(2, new String[] { "a", "b", "c" });

        // 3
        test(3, new String[] { "a", "b", "c" }, "d");

        // 4
        test(4, new int[] { 1, 2, 3 });

    }

    private static void test(int number, Object... args) {

        System.out.println("No." + number + " length = " + args.length);

    }

}

可変長引数のパラメータにいろんなパターンで値を渡して、受け取ったパラメータのサイズを出力しています。
コンソールにはどう出力されるでしょうか?

出力結果

コンソール
No.1 length = 3
No.2 length = 3
No.3 length = 2
No.4 length = 1

解説

可変長引数の定義の実態が配列であることを意識し、コンパイル時にどんな形になるかを考えると分かりやすい。

受け取り側はコンパイル時に下記引数を持つメソッドに変換される。

private static void test(int number, Object[] args) {
    ...
}

No.1

コンパイル時に受け取り側の配列に変換される。

test(1, new Object[]{"a", "b", "c"});

サイズは3となる。

No.2

すでに配列で共変なため、変換が行われない。

test(2, new String[]{"a", "b", "c"});

サイズは3となる。

No.3

コンパイル時に受け取り側の配列に変換される。

test(3, new Object[] { new String[] { "a", "b", "c" }, "d" });

サイズは2となる。

No.4

すでに配列だが、プリミティブ型の配列はそのままObjectの配列にキャストすることが出来ないので、コンパイル時にObjectの配列に包まれた状態に変換される。

test(4, new Object[] { new int[] { 1, 2, 3 } });

サイズは1となる。

あとがき

  • No.2で配列自体を1つのデータとして渡したい場合は以下のように明示的にキャストをすると実現出来る。
//No.2 length = 1
test(2, (Object) new String[]{"a", "b", "c"});
  • 変な動きをして困ったら逆コンパイル(java → class → java)してみるといいかもしれない。 java8対応の逆コンパイラはCFR, FernFlower, Procyonとかあってなんかいろいろ特徴があるみたいです → いつか記事にしたい
11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12