您何时在Java中使用varargs?


192

我怕可变参数。我不知道该怎么用。

另外,让人们随心所欲地传递争论是很危险的。

什么是使用它们的好地方的上下文示例?


3
我不明白为什么它会是“危险的”。这比拥有一个使用不同参数的大量调用方法没有什么危险。您对堆栈有疑问吗?然后,您不应该这样做,因为varargs映射到通过引用传递的数组。
jfpoilpret

66
只是想插话:避免使用您尚未完全满意的语言功能并没有什么错。比使用您不了解的功能要好得多!;)
史蒂文(Steven

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ėnas13年

1
这不是对varargs的批评,但是实际上有一些理由要“害怕”(直到您完全了解varargs的局限性);这就是存在@SafeVarargs批注的原因。stackoverflow.com/a/14252221/1593924
乔恩·库姆斯

Answers:


150

对于需要处理不确定数目的对象的任何方法,Varargs都是有用的。一个很好的例子是。格式字符串可以接受任意数量的参数,因此您需要一种机制来传递任意数量的对象。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

一个好的经验法则是:

“将varargs用于需要T数组(无论T类型如何)作为输入的任何方法(或构造函数)”。

这将使对这些方法的调用更加容易(无需这样做 new T[]{...})。

您可以将此规则扩展为包含带有 List<T>参数的,前提是该参数仅用于输入(即,列表未被该方法修改)。

此外,我将避免使用 f(Object... args)它因为它倾向于使用不清楚的API进行编程。

就示例而言,我在DesignGridLayout中使用了它,在其中可以JComponent在一个调用中添加几个s:

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

在上面的代码中,add()方法定义为add(JComponent... components)

最后,此类方法的实现必须考虑到可以使用空vararg调用它的事实。如果要施加至少一个参数,则必须使用一个丑陋的技巧,例如:

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

我认为这个技巧很丑陋,因为该方法的实现将比仅仅具有 T... args在其参数列表中。

希望这有助于阐明有关可变参数的观点。


1
您是否考虑过添加前提条件检查if (args.length == 0) throw new RuntimeException("foo");?(因为调用方违反了合同)
Micha Wiedenmann

24
好的,一个好的API的目的是为了尽早防止误用,因此在编译时尽可能地避免误用,因此建议void f(T arg1, T... args)始终确保不带任何参数地调用它,而不必等到运行时。
jfpoilpret

我认为大多数情况下,仅调用无参数的varargs函数将完全无济于事。重点是,不向varargs函数提供任何参数很可能不会造成很大的损害。
WorldSEnder

Varargs很有用,但并不等同于使用数组。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()时将所有变量连接在一起的麻烦。但是,如果我使用varargs,则服务器只需在意识到不需要它们之前将它们放入数组即可。节省了大量时间。


4
您可以在超类中实现调试方法,以使用反射来打印任何对象。
Lluis Martinez

12

当我们不确定方法中要传递的参数数量时,可以使用Varargs。它在后台创建了一个未指定长度的参数数组,此类参数在运行时可以视为数组。

如果我们有一个方法被重载以接受不同数量的参数,那么我们可以简单地使用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:即使我不熟悉varargs,我也很害怕使用它。但是现在我已经习惯了。就像说的那样:“我们紧贴已知的事物,害怕未知的事物”,因此,尽可能多地使用它,您也将开始喜欢它:)


4
varargs的C#等效项是“ params”。它也做同样的事情,并接受可变数量的参数。请参阅以下内容以获得更好的理解: dotnetperls.com/params
Sarvan 2012年

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

3

总和是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

2
您永远不会知道...特别是当参数的数量在调用方不是经过硬编码的,而是还可以将语句收集到循环中的列表中时,传入数组并不是很常见。
Thilo

5
我认为您的用例示例通常不会出现。有人可能会争辩说,您的API不应以这种方式使用输入数组,如果使用,则必须对其进行记录。
劳伦斯·多尔

重载同时支持这两种方法的方法;argMethod(Object .. objList)argMethod(Object []
objList

2
@thetoolman:我认为不可能。它将被视为重复方法。
Thilo 2012年

1

我经常将varargs用于可以采用某种过滤器对象的构造函数。例如,我们基于Hadoop的系统的很大一部分是基于Mapper,该Mapper将项目的序列化和反序列化处理为JSON,并应用多个处理器,每个处理器获取一个内容项,然后修改并返回它,或者返回null拒绝。


1

在Java文档Var-Args中,很清楚var args的用法:

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

关于用法,它说:

“因此,什么时候应该使用varargs?作为客户端,只要API提供了它们,就应该利用它们。核心API的重要用途包括反射,消息格式和新的printf工具。作为API设计人员,您应该使用它们只能在收益真正引人注意的情况下,才应谨慎使用。一般而言,您不应重载varargs方法,否则程序员将很难弄清楚将调用哪个重载。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.