Java two varargs in one method

谁说我不能喝 提交于 2019-11-26 09:05:29

问题


Is there any way in java, to create a method, which is expecting two different varargs? 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!


回答1:


Only one vararg, sorry. But using asList() makes it almost as convenient:

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

 -----------

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



回答2:


In Java, only one varargs argument is allowed and it must be the last parameter of the signature.

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



回答3:


A possible API design in which the calling code looks like

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

through "fluent" 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(...)

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

Maybe this is a little better

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



回答4:


Only one vararg is allowed. This is because multiple vararg arguments are ambiguous. 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? 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. This confusion is why only one varargs is allowed.

varargs must also appear at the end of a method declaration.

Just declare the specific type instead as 2 arrays.




回答5:


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. 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). 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.




回答6:


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



回答7:


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.

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



回答8:


It is not possible because the Java Language Specification says so (see 8.4.1. Formal Parameters):

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). 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.




回答9:


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.

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.




回答10:


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



回答11:


follwing on Lemuel Adane (cant comment on the post, due to lack of rep :))

if you use

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

then you may loop using How to determine an object's class (in 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.




回答12:


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


来源:https://stackoverflow.com/questions/17868059/java-two-varargs-in-one-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!