いつJavaで可変引数を使用しますか?


192

varargsが怖いです。何に使うかわかりません。

さらに、人々が望むだけ多くの議論を通過させることは危険だと感じます。

それらを使用するのに適したコンテキストの例は何ですか?


3
なぜ「危険」なのかはわかりません。異なる引数でメソッドを何度も呼び出すよりも危険ではありません。スタックに懸念がありますか?varargsは参照によって渡される配列にマップされるため、そうしないでください。
jfpoilpret 2009

66
ただ口にしたかったのですが、(まだ)十分に使い慣れていない言語機能を回避しても問題はありません。理解できない機能を使用するよりもはるかに優れています。;)
スティーブン

2
未知を恐れることは正常です。varargsの詳細については、docs.oracle.com/javase/tutorial/java/javaOO/arguments.htmlを参照してください。varargsを恐れることはありません。Varargsは便利です。varargsの使用方法を理解すると、[PrintStream.format]( " docs.oracle.com/javase/7/docs/api/java/io/…、java.lang.Object ...)"のようなメソッドを作成することができます。 ):)
開発者MariusŽilėnas2013年

1
これは可変引数の批判ではありませんが、実際には「恐れる」理由がいくつかあります(可変引数の制限を正確に理解するまで)。@SafeVarargsアノテーションが存在するのはこのためです。stackoverflow.com/a/14252221/1593924
Jon Coombs

回答:


150

Varargsは、不確定な数のオブジェクトを処理する必要があるすべてのメソッドに役立ちます。良い例が1つですString.format。フォーマット文字列は任意の数のパラメーターを受け入れることができるため、任意の数のオブジェクトを渡すメカニズムが必要です。

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);

5
配列パラメーターは、不確定な数のオブジェクトを受け取ることもできますが、varargsパラメーターを使用すると、呼び出し時に柔軟性と利便性が向上します。配列を構築して渡すコードを記述したり、varargsパラメーターで受け取ることを選択したときにJavaに代わって実行させることができます。
H2ONaCl

79

大まかな目安は次のとおりです。

「入力としてTの配列(T型が何であれ)を必要とするメソッド(またはコンストラクター)には可変引数を使用します。

これにより、これらのメソッドの呼び出しが簡単になります(実行する必要はありません) new T[]{...})。

このルールを拡張して、 List<T>引数をます。ただし、この引数が入力専用である場合(つまり、リストがメソッドによって変更されない場合)。

さらに、私は使用を控えます f(Object... args) APIが不明確なプログラミング方法に陥ってしまうため、ます。

例として、DesignGridLayoutで使用しました。1JComponentの呼び出しで複数のを追加できます。

layout.row().grid(new JLabel("Label")).add(field1, field2, field3);

上記のコードでは、add()メソッドは次のように定義されています。 add(JComponent... components)ます。

最後に、そのようなメソッドの実装は、空の可変引数で呼び出される可能性があるという事実に注意する必要があります!少なくとも1つの引数を課したい場合は、次のような醜いトリックを使用する必要があります。

void f(T arg1, T... args) {...}

メソッドの実装は、 T... args引数リストにあるています。

これが可変引数に関する要点を明確にするのに役立つことを願っています。


1
if (args.length == 0) throw new RuntimeException("foo");代わりに前提条件チェックを追加することを検討しましたか?(呼び出し側が契約に違反していたため)
Micha Wiedenmann 2013

24
まあ、良いAPIの目的は、誤用をできる限り早く防ぐことです。したがって、可能な場合はコンパイル時に、void f(T arg1, T... args)実行時まで待機する必要なく、常に引数なしで呼び出されることがないようにすることをお勧めします。
jfpoilpret 2013年

ほとんどの場合、引数なしでvarargsのみの関数を呼び出すと、まったく何も実行されません。ポイントは、varargs関数に引数を指定しなくても、ほとんど害はないということです。
WorldSEnder 2016年

可変引数は便利ですが、配列を使用することと同等ではありません。Varargsは変更できません。したがって、ジェネリックスと同様に、型の消去の影響を受けますが、ジョブによっては許容できない制約になる可能性があります。
ジュリアン

34

デバッグの目的でログに出力するためにvarargsを頻繁に使用しています。

アプリのほぼすべてのクラスにdebugPrint()メソッドがあります。

private void debugPrint(Object... msg) {
    for (Object item : msg) System.out.print(item);
    System.out.println();
}

次に、クラスのメソッド内で、次のような呼び出しがあります。

debugPrint("for assignment ", hwId, ", student ", studentId, ", question ",
    serialNo, ", the grade is ", grade);

コードが機能していることを確認したら、debugPrint()メソッドのコードをコメント化して、ログに無関係で不要な情報が多く含まれないようにしますが、debugPrint()への個々の呼び出しはコメント化しないでおくことができます。後でバグを見つけたら、debugPrint()コードのコメントを外すだけで、debugPrint()へのすべての呼び出しが再びアクティブになります。

もちろん、varargsを簡単に回避して、代わりに次のようにすることもできます。

private void debugPrint(String msg) {
    System.out.println(msg);
}

debugPrint("for assignment " + hwId + ", student " + studentId + ", question "
    + serialNo + ", the grade is " + grade);

ただし、この場合、debugPrint()コードをコメントアウトすると、サーバーは、結果の文字列で何も行われなくても、debugPrint()を呼び出すたびにすべての変数を連結するという問題を経験する必要があります。ただし、可変引数を使用する場合、サーバーはそれらを必要としないことに気付く前に、それらを配列に入れるだけで済みます。多くの時間が節約されます。


4
スーパークラスにデバッグメソッドを実装して、リフレクションを使用してオブジェクトを印刷できます。
Lluis Martinez

12

メソッドで渡される引数の数がわからない場合は、可変引数を使用できます。それはバックグラウンドで不特定の長さのパラメータの配列を作成し、そのようなパラメータは実行時に配列として扱うことができます。

異なる数のパラメーターを受け入れるためにオーバーロードされるメソッドがある場合、メソッドを異なる時間にオーバーロードする代わりに、varargsコンセプトを使用できます。

また、パラメーターのタイプが異なる場合は、「Object ... test」を使用するとコードが大幅に簡略化されます。

例えば:

public int calculate(int...list) {
    int sum = 0;
    for (int item : list) {
        sum += item;
    }
    return sum;
}

ここでは、間接的にint型(リスト)の配列がパラメーターとして渡され、コードでは配列として扱われます。

理解を深めるには、次のリンクに従ってください(この概念を明確に理解するのに大いに役立ちました)。http//www.javadb.com/using-varargs-in-java

PS:私はそれを知っていなかったときに可変引数の使用を恐れていました。しかし、今はそれに慣れています。言われているように、「私たちは既知のものに固執し、未知のものを恐れています」ので、できるだけ多く使用するだけで、あなたも好きになります:)


4
C#varargsに相当するものは「params」です。また、同じことを行い、可変数のパラメーターを受け入れます。より良い理解のためにこれを参照してください。 dotnetperls.com/params
Sarvan

11

Varargsは、Javaバージョン1.5で追加された機能です。

なぜこれを使うのですか?

  1. メソッドに渡す引数の数がわからない場合はどうなりますか?
  2. メソッドに無制限の数の引数を渡したい場合はどうなりますか?

これはどのように機能しますか?

指定された引数で配列を作成し、その配列をメソッドに渡します。

例:

public class Solution {



    public static void main(String[] args) {
        add(5,7);
        add(5,7,9);
    }

    public static void add(int... s){
        System.out.println(s.length);
        int sum=0;
        for(int num:s)
            sum=sum+num;
        System.out.println("sum is "+sum );
    }

}

出力:

2

合計は12

合計は21


6

私もvarargs関連の恐れがあります:

呼び出し元が明示的な配列を(複数のパラメーターではなく)メソッドに渡すと、その配列への共有参照を受け取ります。

この配列を内部的に格納する必要がある場合は、最初にそれを複製して、呼び出し元が後で変更できないようにすることができます。

 Object[] args = new Object[] { 1, 2, 3} ;

 varArgMethod(args);  // not varArgMethod(1,2,3);

 args[2] = "something else";  // this could have unexpected side-effects

これは、状態が後で変化する可能性のある種類のオブジェクトを渡すことと実際には違いはありませんが、通常、配列は(配列ではなく複数の引数を使用した呼び出しの場合)コンパイラによって内部的に作成された安全な新しい配列であるため使用すると、これは確かに予期しない動作です。


1
正解ですが、この例は少々難解です。これはおそらくこの方法であなたのAPIを使用するでしょうか?
jfpoilpret 2009

2
あなたは決してわかりません...そして特に、引数の数が呼び出し側でハードコードされていない場合、ループ内のリストに収集された言い回しがある場合、配列を渡すことはそれほど珍しいことではありません。
ティロ

5
あなたのユースケースの例は、一般的に言えば、ありそうなことではないと思います。APIがこのように入力配列を使用すべきではないと主張する人もいるかもしれません。使用する場合は、それを文書化する必要があります。
Lawrence Dol、

メソッドをオーバーロードして両方をサポートします。argMethod(Object .. objList)argMethod(Object [] objList)
thetoolman

2
@thetoolman:それは可能ではないと思います。重複したメソッドと見なされます。
Thilo

1

ある種のフィルターオブジェクトを取得できるコンストラクターでは、可変引数を頻繁に使用します。たとえば、Hadoopに基づくシステムの大部分は、アイテムのシリアル化と逆シリアル化をJSONに処理するMapperに基づいており、それぞれがコンテンツのアイテムを取得して変更して返すか、nullを返す多数のプロセッサを適用します拒否する。


1

Var-ArgsのJavaドキュメントでは、var argsの使用法は非常に明確です。

http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html

それが言う使用法について:

「それで、いつvarargsを使うべきですか?クライアントとして、APIがそれらを提供するときはいつでもそれらを利用する必要があります。コアAPIの重要な用途には、リフレクション、メッセージのフォーマット、および新しいprintf機能が含まれます。APIデザイナーとして、それらを使用する必要があります控えめに言っても、メリットが本当に説得力のある場合に限られます。一般的に、varargsメソッドをオーバーロードしてはなりません。そうしないと、プログラマーがどのオーバーロードが呼び出されるかを理解するのが難しくなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.