简体   繁体   English

Java在一种方法中使用两个可变参数

[英]Java two varargs in one method

Is there any way in java to create a method, which is expecting two different varargs? java中是否有任何方法可以创建一个方法,该方法需要两个不同的可变参数? I know, with the same object kind it isn't possible because the compiler does'nt know where to start or to end.我知道,使用相同的对象类型是不可能的,因为编译器不知道从哪里开始或结束。 But why it also is'nt possible with to different Object types?但是为什么不同的对象类型也不可能呢?

For example:例如:

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

Is there any way to create method like this?有没有办法创建这样的方法? Thank you!谢谢!

Only one vararg, sorry.只有一个可变参数,对不起。 But using asList() makes it almost as convenient:但是使用 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));

In Java, only one varargs argument is allowed and it must be the last parameter of the signature.在 Java 中,只允许一个varargs参数,并且它必须是签名的最后一个参数。

But all it does it convert it to an array anyway, so you should just make your two parameters explicit arrays:但它所做的一切都是将它转换为数组,所以你应该让你的两个参数显式数组:

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

A possible API design in which the calling code looks like一种可能的 API 设计,其中调用代码看起来像

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

through "fluent" API通过“流利的”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)
{
    ...
}

The danger is if the programmer forgot to call with(...)危险是如果程序员忘记调用with(...)

    doSomething("a", "b");  // nothing is done

Maybe this is a little better也许这会好一点

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

Only one vararg is allowed.只允许使用一个vararg This is because multiple vararg arguments are ambiguous.这是因为多个vararg参数是不明确的。 For example, what if you passed in two varargs of the same class?例如,如果你传入同一个类的两个可变参数怎么办?

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

Where does args1 end and args2 begin? args1 结束和 args2 从哪里开始? Or how about something more confusing here.或者在这里更令人困惑的东西怎么样。

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

ChildClass extends SuperClass, and so is can legally exist in args1, or args2. ChildClass 扩展了 SuperClass,因此它可以合法地存在于 args1 或 args2 中。 This confusion is why only one varargs is allowed.这种混淆就是为什么只允许一个varargs的原因。

varargs must also appear at the end of a method declaration. varargs也必须出现在方法声明的末尾。

Just declare the specific type instead as 2 arrays.只需将特定类型声明为 2 个数组即可。

Although this kind of thing is occasionally useful, usually if you find that you are hitting a restriction in Java you could probably redesign something and come out much better.虽然这种东西偶尔有用,但通常如果你发现你在 Java 中遇到了限制,你可能会重新设计一些东西并做得更好。 Here are some possible other ways to look at it...这里有一些可能的其他方式来看待它......

If the two lists are related at all you probably want to create a wrapper class for the two different lists and pass in the wrapper.如果这两个列表完全相关,您可能希望为两个不同的列表创建一个包装器类并传入包装器。 Wrappers around collections are almost always a good idea--they give you a place to add code that relates to the collection.集合周围的包装器几乎总是一个好主意——它们为您提供了一个添加与集合相关的代码的地方。

If this is a way to initialize data, parse it from a string.如果这是一种初始化数据的方法,请从字符串中解析它。 For instance, "abc, 123:def, 456:jhi,789" is almost embarassingly easy to split up with 2 split statements and a loop (2-3 lines of code).例如,“abc, 123:def, 456:jhi,789” 使用 2 个拆分语句和一个循环(2-3 行代码)几乎非常容易拆分。 You can even make a little custom parser class that parses a string like that into a structure you feed into your method.您甚至可以创建一个小的自定义解析器类,将类似的字符串解析为您提供给方法的结构。

Hmm--honestly asside from initializing data I don't even know why you'd want to do this anyway, any other case and I expect you'd be passing in 2 collections and wouldn't be interested in varags at all.嗯——老实说,除了初始化数据之外,我什至不知道你为什么要这样做,在任何其他情况下,我希望你会传入 2 个集合并且根本不会对 varags 感兴趣。

You can do something like this, then you can cast and add additional logic inside that method.你可以做这样的事情,然后你可以在该方法中强制转换和添加额外的逻辑。

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

And use this method like so:并像这样使用这种方法:

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

This is an old thread, but I thought this would be helpful regardless.这是一个旧线程,但我认为无论如何这都会有所帮助。

The solution I found isn't very neat but it works.我发现的解决方案不是很整洁,但它有效。 I created a separate class to handle the heavy lifting.我创建了一个单独的类来处理繁重的工作。 It only has the two variables I needed and their getters.它只有我需要的两个变量和它们的吸气剂。 The constructor handles the set methods on its own.构造函数自己处理 set 方法。

I needed to pass direction objects and a respective Data object.我需要传递方向对象和相应的数据对象。 This also solves the possible problem of uneven data pairs, but that is probably only for my usage needs.这也解决了可能出现的数据对不均匀的问题,但这可能只是为了我的使用需求。

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

I would then just pass this class as the vararg for the method然后我将这个类作为方法的可变参数传递

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

It is not possible because the Java Language Specification says so (see 8.4.1. Formal Parameters ):这是不可能的,因为 Java 语言规范是这么说的(参见8.4.1. 形式参数):

The last formal parameter of a method or constructor is special: it may be a variable arity parameter , indicated by an ellipsis following the type.方法或构造函数的最后一个形式参数是特殊的:它可能是一个可变参数,由类型后面的省略号表示。

Note that the ellipsis (...) is a token unto itself (§3.11).请注意,省略号 (...) 本身就是一个标记(第 3.11 节)。 It is possible to put whitespace between it and the type, but this is discouraged as a matter of style.可以在它和类型之间放置空格,但出于风格问题,不鼓励这样做。

If the last formal parameter is a variable arity parameter, the method is a variable arity method .如果最后一个形参是可变参数,则方法是可变参数方法 Otherwise, it is a fixed arity method .否则,它是一个固定数量的方法

As to why only one and only the last parameter, that would be a guess, but probably because allowing that could lead to undecidable or ambiguous problems (eg consider what happens with method(String... strings, Object... objects) ), and only allowing non-intersecting types would lead to complications (eg considering refactorings where previously non-intersecting types suddenly are), lack of clarity when it does or does not work, and complexity for the compiler to decide when it is applicable or not.至于为什么只有一个且只有最后一个参数,那将是一个猜测,但可能是因为允许这样做可能会导致无法确定或模棱两可的问题(例如,考虑使用method(String... strings, Object... objects)会发生什么) , 并且只允许不相交的类型会导致复杂化(例如考虑重构以前不相交的类型突然出现的情况),当它起作用或不起作用时缺乏清晰度,以及编译器决定何时适用或不适用的复杂性.

I just read another question about this "pattern", but it is already removed, so I would like to propose a different approach to this problem, as I didn't see here this solution.我刚刚阅读了关于这个“模式”的另一个问题,但它已经被删除了,所以我想提出一种不同的方法来解决这个问题,因为我在这里没有看到这个解决方案。

Instead to force the developer to wrapping the inputs parameter on List or Array, it will be useful to use a "curry" approach, or better the builder pattern.代替强制开发人员将输入参数包装在 List 或 Array 上,使用“curry”方法或更好的构建器模式将很有用。

Consider the following code:考虑以下代码:

/**
 * 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();

    }

}

As you can see you in this way, you could add new elements of which type you need when you need.正如您以这种方式看到的那样,您可以在需要时添加所需类型的新元素。

All the implementation is wrapped.所有的实现都被包装了。

If you are not going to be passing a large number of Strings most of the time for the first argument you could provide a bunch of overloads that take different numbers of Strings and wrap them in an array before calling a method that takes the array as the first argument.如果您不打算在大多数时间为第一个参数传递大量字符串,您可以提供一堆重载,这些重载采用不同数量的字符串并将它们包装在一个数组中,然后再调用一个将数组作为第一个论点。

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) {
    // ...
    // ...
}

follwing on Lemuel Adane (cant comment on the post, due to lack of rep :))关注 Lemuel Adane (无法评论该帖子,由于缺乏代表 :))

if you use如果你使用

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

then you may loop using How to determine an object's class (in Java)?那么您可以循环使用如何确定对象的类(在 Java 中)?

like for instance例如

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

or anything you intend to do.或您打算做的任何事情。

You can convert your varargs to arrays您可以将可变参数转换为数组

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

then with some helper methods to convert your varargs to array like this:然后使用一些辅助方法将您的可变参数转换为数组,如下所示:

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

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

Then you can use those helper methods to convert your vararged parameters.然后,您可以使用这些辅助方法来转换您的可变参数。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM