web-dev-qa-db-ja.com

Javaで可変引数を持つメソッドに引数として配列を渡すことはできますか?

以下のような関数を作成できるようにしたいです。

class A {
  private String extraVar;
  public String myFormat(String format, Object ... args){
    return String.format(format, extraVar, args);
  }
}

ここでの問題は、メソッドargsではmyFormatObject[]として扱われるため、Object内のすべてのargsが新しい引数として渡されるようにしたいのですが、String.formatへの単一の引数であることです。 String.formatも可変引数を持つメソッドなので、これは可能なはずです。

これが不可能な場合、String.format(String format, Object[] args)のような方法はありますか?その場合は、新しい配列を使用してextraVarargsの前に追加し、それをそのメソッドに渡すことができます。

242
user362382

可変個メソッドの基礎となる型function(Object... args)isfunction(Object[] args)。 Sunは、下位互換性を保つために、この方法で可変引数を追加しました。

ですから、extraVarargsの前に追加してString.format(format, args)を呼び出すことができるはずです。

164
jasonmp85

はい、T...T[]の単なる構文上の糖です。

JLS 8.4.1フォーマットパラメータ

リストの最後の仮パラメータは特別です。それは可変アリティパラメータであり、型に続く省略記号で示されます。

最後の仮パラメータがT型の可変アリティパラメータである場合、それは型T[]の仮パラメータを定義すると見なされます。その場合の方法は可変アリティ方法です。それ以外の場合は、固定アリティメソッドです。可変アリティメソッドの呼び出しは、仮パラメータよりも実際の引数式を多く含むことがあります。変数arityパラメータの前の仮パラメータに対応しないすべての実引数式が評価され、結果がメソッド呼び出しに渡される配列に格納されます。

これは説明のための例です。

public static String ezFormat(Object... args) {
    String format = new String(new char[args.length])
        .replace("\0", "[ %s ]");
    return String.format(format, args);
}
public static void main(String... args) {
    System.out.println(ezFormat("A", "B", "C"));
    // prints "[ A ][ B ][ C ]"
}

はい、上記のmainメソッドは有効です。これもString...は単なるString[]であるためです。また、配列は共変であるため、String[]Object[]であるため、どちらの方法でもezFormat(args)を呼び出すことができます。

また見なさい


#1:nullを渡す

可変引数の解決方法は非常に複雑であり、時にはそれはあなたを驚かせることがあります。

この例を考えてください。

static void count(Object... objs) {
    System.out.println(objs.length);
}

count(null, null, null); // prints "3"
count(null, null); // prints "2"
count(null); // throws Java.lang.NullPointerException!!!

可変引数がどのように解決されるかに起因して、最後のステートメントはobjs = nullで呼び出します。もちろん、これはobjs.lengthNullPointerExceptionを引き起こします。 varargsパラメータに1つのnull引数を与えたい場合は、次のいずれかを実行できます。

count(new Object[] { null }); // prints "1"
count((Object) null); // prints "1"

関連する質問

以下は、可変引数を扱う際に人々が尋ねたいくつかの質問の例です。


#2:追加の引数を追加する

ご存知のとおり、次のものは「機能しません」。

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(myArgs, "Z"));
    // prints "[ [Ljava.lang.String;@13c5982 ][ Z ]"

可変引数の機能上の理由から、ezFormatは実際には2つの引数を取ります。最初の引数はString[]、2番目の引数はStringです。配列をvarargsに渡していて、その要素を個々の引数として認識させたい場合、さらに引数を追加する必要がある場合は、別のを作成する以外に選択肢はありません。追加の要素を収容する配列

これが便利なヘルパーメソッドです。

static <T> T[] append(T[] arr, T lastElement) {
    final int N = arr.length;
    arr = Java.util.Arrays.copyOf(arr, N+1);
    arr[N] = lastElement;
    return arr;
}
static <T> T[] prepend(T[] arr, T firstElement) {
    final int N = arr.length;
    arr = Java.util.Arrays.copyOf(arr, N+1);
    System.arraycopy(arr, 0, arr, 1, N);
    arr[0] = firstElement;
    return arr;
}

今、あなたは以下をすることができます:

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(append(myArgs, "Z")));
    // prints "[ A ][ B ][ C ][ Z ]"

    System.out.println(ezFormat(prepend(myArgs, "Z")));
    // prints "[ Z ][ A ][ B ][ C ]"

#3:プリミティブの配列を渡す

それは "動作"しません:

    int[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ [I@13c5982 ]"

可変引数は参照型でのみ機能します。オートボクシングは、プリミティブの配列には適用されません。次のように動作します。

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
291

配列を渡しても構いません - 実際、同じものになります。

String.format("%s %s", "hello", "world!");

と同じです

String.format("%s %s", new Object[] { "hello", "world!"});

基本的なメソッドが vararg パラメータの配列を期待しているため、コンパイラは最初の構文を2番目の構文に変換します。

見る

21
mdma

jasonmp85は別の配列をString.formatに渡すことについて正しいです。一度構築した配列のサイズは変更できないので、既存の配列を変更するのではなく、新しい配列を渡す必要があります。

Object newArgs = new Object[args.length+1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = extraVar; 
String.format(format, extraVar, args);
4
ebelisle

私は同じ問題を抱えていました。

String[] arr= new String[] { "A", "B", "C" };
Object obj = arr;

そして、objをvarargs引数として渡しました。出来た。

1
Srijit Paul