39

java中有什么方法可以创建一个需要两个不同的可变参数的方法吗?我知道,使用相同的对象类型是不可能的,因为编译器不知道从哪里开始或结束。但是为什么不同的对象类型也不可能呢?

例如:

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

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

4

12 回答 12

41

只有一个可变参数,对不起。但是使用 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));
于 2013-07-25T20:28:12.853 回答
19

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

但它所做的一切都是将它转换为数组,所以你应该让你的两个参数显式数组:

public void doSomething(String[] s, int[] i){
于 2013-07-25T20:25:40.460 回答
11

一种可能的 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();
于 2013-07-25T22:49:59.410 回答
5

vararg允许一个。这是因为多个vararg参数是模棱两可的。例如,如果你传入同一个类的两个可变参数怎么办?

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

args1 结束和 args2 从哪里开始?或者在这里更令人困惑的东西怎么样。

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

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

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

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

于 2013-07-25T20:27:48.243 回答
4

虽然这种东西偶尔有用,但通常如果你发现你在 Java 中遇到了限制,你可能会重新设计一些东西并做得更好。这里有一些可能的其他方式来看待它......

如果这两个列表完全相关,您可能希望为两个不同的列表创建一个包装器类并传入包装器。集合周围的包装器几乎总是一个好主意——它们为您提供了一个添加与集合相关的代码的地方。

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

嗯——老实说,除了初始化数据之外,我什至不知道你为什么要这样做,在任何其他情况下,我希望你会传入 2 个集合并且根本不会对 varags 感兴趣。

于 2013-07-25T20:40:43.480 回答
2

你可以做这样的事情,然后你可以在该方法中强制转换和添加额外的逻辑。

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

并像这样使用这种方法:

doSomething(stringValue1, stringValue2, intValue1, intValue2,         
    intValue3);
于 2016-03-22T03:34:25.153 回答
1

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

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

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

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;
    }
}

然后我将这个类作为方法的可变参数传递

public void method(DataDirectionPair... ndPair){
    for(DataDirectionPair temp : ndPair){
        this.node = temp.getNode();
        this.direction = temp.getDir();
        //or use it however you want
    }
}
于 2014-11-17T16:08:22.487 回答
1

这是不可能的,因为 Java 语言规范是这么说的(参见8.4.1. 形式参数):

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

请注意,省略号 (...) 本身就是一个标记(第 3.11 节)。可以在它和类型之间放置空格,但出于风格问题,不鼓励这样做。

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

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

于 2018-01-18T10:24:17.080 回答
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();

    }

}

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

所有的实现都被包装了。

于 2016-07-25T13:20:49.553 回答
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) {
    // ...
    // ...
}
于 2016-08-07T14:14:06.003 回答
0

关注 Lemuel Adane (无法评论该帖子,由于缺乏代表 :))

如果你使用

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

那么您可以循环使用如何确定对象的类(在 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);
}

或您打算做的任何事情。

于 2016-08-17T15:45:39.157 回答
0

您可以将可变参数转换为数组

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

然后使用一些辅助方法将您的可变参数转换为数组,如下所示:

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));
于 2017-07-04T08:26:38.227 回答