Java两种方法中的varargs

时间:2013-07-25 20:23:50

标签: java arrays variadic-functions

在java中有没有办法创建一个方法,期待两种不同的变量? 我知道,由于编译器不知道从哪里开始或结束,因此使用相同的对象类型是不可能的。但为什么它也不可能与不同的对象类型?

例如:

public void doSomething(String... s, int... i){
    //...
    //...
}

有没有办法创建这样的方法? 谢谢!

12 个答案:

答案 0 :(得分:33)

只有一个vararg,抱歉。但是使用asList()使它几乎同样方便:

 public void myMethod(List<Integer> args1, List<Integer> args2) {
   ...
 }

 -----------

 import static java.util.Arrays.asList;
 myMethod(asList(1,2,3), asList(4,5,6));

答案 1 :(得分:17)

在Java中,只允许一个varargs参数,它必须是签名的最后一个参数。

但无论如何它只是将它转换为数组,所以你应该让你的两个参数显式数组:

public void doSomething(String[] s, int[] i){

答案 2 :(得分:10)

一种可能的API设计,其中调用代码看起来像

    doSomething("a", "b").with(1,2);

通过“流畅的”API

public Intermediary doSomething(String... strings)
{
    return new Intermediary(strings);
}

class Intermediary
{
    ...
    public void with(int... ints)
    {
        reallyDoSomething(strings, ints);
    }
}

void reallyDoSomething(String[] strings, int[] ints)
{
    ...
}

如果程序员忘记拨打with(...)

,则存在危险
    doSomething("a", "b");  // nothing is done

也许这会好一点

    with("a", "b").and(1, 2).doSomething();

答案 3 :(得分:4)

只允许一个vararg。这是因为多个vararg参数不明确。例如,如果你传入同一类的两个varargs怎么办?

public void doSomething(String...args1, String...args2);

args1 end和args2从哪里开始?或者这里有些令人困惑的事情。

class SuperClass{}
class ChildClass extends SuperClass{}
public void doSomething(SuperClass...args1, ChildClass...args2);

ChildClass扩展了SuperClass,因此可以合法地存在于args1或args2中。这种混淆是为什么只允许一个varargs的原因。

varargs也必须出现在方法声明的末尾。

只需将特定类型声明为2个数组。

答案 4 :(得分:4)

虽然这种事情偶尔会有用,但通常如果你发现你在Java中受到限制,你可能会重新设计一些东西并且会更好。以下是一些其他可能的方法......

如果两个列表完全相关,您可能想要为两个不同的列表创建一个包装类并传入包装器。围绕集合的包装几乎总是一个好主意 - 它们为您提供了添加与集合相关的代码的位置。

如果这是一种初始化数据的方法,请从字符串中解析它。例如,“abc,123:def,456:jhi,789”几乎非常容易分割出2个分割语句和一个循环(2-3行代码)。您甚至可以创建一个小的自定义解析器类,将类似的字符串解析为您提供给方法的结构。

嗯 - 老老实实地从初始化数据中得到帮助我甚至都不知道你为什么要这样做,任何其他情况,我希望你能通过2个系列而不会对varags感兴趣所有

答案 5 :(得分:2)

您可以执行类似的操作,然后您可以在该方法中强制转换和添加其他逻辑。

public void doSomething(Object... stringOrIntValues) {
    ...
    ...
}

并像这样使用这种方法:

doSomething(stringValue1, stringValue2, intValue1, intValue2,         
    intValue3);

答案 6 :(得分:1)

这是一个老线程,但我认为无论如何这都会有所帮助。

我找到的解决方案不是很整洁但是有效。我创建了一个单独的类来处理繁重的工作。它只有我需要的两个变量和它们的吸气剂。构造函数自己处理set方法。

我需要传递方向对象和相应的Data对象。这也解决了数据对不均匀的问题,但这可能只是为了我的使用需求。

public class DataDirectionPair{

    Data dat;
    Directions dir;

    public DataDirectionPair(Data dat, Directions dir) {
        super();
        this.dat = dat;
        this.dir = dir;
    }

    /**
     * @return the node
     */
    public Node getNode() {
        return node;
    }

    /**
     * @return the direction
     */
    public Directions getDir() {
        return dir;
    }
}

然后我会把这个类作为方法的vararg传递给

public void method(DataDirectionPair... ndPair){
    for(DataDirectionPair temp : ndPair){
        this.node = temp.getNode();
        this.direction = temp.getDir();
        //or use it however you want
    }
}

答案 7 :(得分:1)

这是不可能的,因为Java语言规范是这样说的(参见8.4.1. Formal Parameters):

  

方法或构造函数的最后一个形式参数是特殊的:它   可以是变量arity参数,由省略号表示   按照类型。

     

请注意,省略号(...)本身就是一个标记(§3.11)。可以在它和类型之间放置空格,但这是   沮丧是一种风格问题。

     

如果最后一个形式参数是变量arity参数,则该方法   是一个变量arity方法。否则,它是固定arity方法

至于为什么只有一个而且只有最后一个参数,这可能是一个猜测,但可能是因为允许这可能导致不可判定或模棱两可的问题(例如考虑method(String... strings, Object... objects)会发生什么),并且只允许非交叉类型会导致复杂化(例如,考虑以前非交叉类型突然出现的重构),当它有效或无效时缺乏清晰度,以及编译器决定何时适用的复杂性。

答案 8 :(得分:0)

我刚刚阅读了关于这个“模式”的另一个问题,但它已被删除,所以我想提出一个不同的方法解决这个问题,因为我没有在这里看到这个解决方案。

相反,强制开发人员将输入参数包装在List或Array上,使用“curry”方法或更好的构建器模式将非常有用。

请考虑以下代码:

/**
 * Just a trivial implementation
 */
public class JavaWithCurry {

    private List<Integer> numbers = new ArrayList<Integer>();
    private List<String> strings = new ArrayList<String>();

    public JavaWithCurry doSomething(int n) {

        numbers.add(n);

        return this;
    }

    public JavaWithCurry doSomething(String s) {

        strings.add(s);

        return this;

    }

    public void result() {

        int sum = -1;

        for (int n : numbers) {
            sum += n;
        }


        StringBuilder out = new StringBuilder();

        for (String s : strings) {

            out.append(s).append(" ");

        }

        System.out.println(out.toString() + sum);

    }

    public static void main(String[] args) {

        JavaWithCurry jwc = new JavaWithCurry();

        jwc.doSomething(1)
                .doSomething(2)
                .doSomething(3)
                .doSomething(4)
                .doSomething(5)
                .doSomething("a")
                .doSomething("b")
                .doSomething("c")
                .result();

    }

}

正如您可以通过这种方式看到的那样,您可以根据需要添加所需类型的新元素。

所有实施都已包装。

答案 9 :(得分:0)

如果你不打算在第一个参数的大部分时间内传递大量的字符串,你可以提供一堆重载,这些重载使用不同数量的字符串并将它们包装在一个数组中,然后再调用一个方法来获取数组作为第一个参数。

public void doSomething(int... i){
    doSomething(new String[0], i);
}
public void doSomething(String s, int... i){
    doSomething(new String[]{ s }, i);
}
public void doSomething(String s1, String s2, int... i){
    doSomething(new String[]{ s1, s2 }, i);
}
public void doSomething(String s1, String s2, String s3, int... i){
    doSomething(new String[]{ s1, s2, s3 }, i);
}
public void doSomething(String[] s, int... i) {
    // ...
    // ...
}

答案 10 :(得分:0)

接下来Lemuel Adane(由于缺乏代表而无法对帖子发表评论:))

如果您使用

public void f(Object... args){}

然后你可以使用How to determine an object's class (in Java)?

循环

就像例如

{
   int i = 0;
   while(i< args.length && args[i] instanceof String){
         System.out.println((String) args[i]);
         i++ ;
   }
   int sum = 0;
   while(i< args.length){
         sum += (int) args[i];
         i++ ;
   }
   System.out.println(sum);
}

或您打算做的任何事情。

答案 11 :(得分:0)

您可以将varargs转换为数组

public void doSomething(String[] s, int[] i) {
    ...
}

然后使用一些帮助方法将varargs转换为数组,如下所示:

public static int[] intsAsArray(int... ints) {
    return ints;
}

public static <T> T[] asArray(T... ts) {
    return ts;
}

然后您可以使用这些辅助方法来转换您的变容器参数。

doSomething(asArray("a", "b", "c", "d"), intsAsArray(1, 2, 3));