59

I am not sure of the vocabulary I am using here, please correct me if I'm wrong.

In Javascript, I had the following code:

let args = [1,2,3];

function doSomething (a, b, c) {
    return a + b + c;
}

doSomething(...args);

As you can see, when calling doSomething, I am able to use the ... spread operator in order to "transform" my arguments into 1, 2, 3.

Now, I'm trying to do the same thing with Java.

Let's say I have a Foo class:

public class Foo {
    public int doSomething (int a, int b, int c) {
        return a + b + c;
    }
}

And now I want to call the doSomething:

int[] args = {1, 2, 3};

I'd like to use something like doSomething (...args) instead of calling doSomething(args[0], args[1], args[2]).

I saw that this is possible in the declaration of functions, but I'd like not to change the implementation of such a function.

Hammerbot
  • 15,696
  • 9
  • 61
  • 103
  • 15
    Can't do that. Sorry. – Joe C Sep 24 '17 at 09:36
  • 4
    There is a good reason why this is not possible in Java. You can have another method that takes say 4 integer arguments. You would not know at compile time which one to call since it depends on the length of the list which is only known at run time. – Henry Sep 24 '17 at 09:41
  • 1
    To some Java's [varargs](https://stackoverflow.com/questions/766559/when-do-you-use-varargs-in-java) might look like [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) of JavaScript. But they are **NOT** related. – BlackBeard Jan 09 '18 at 07:12
  • 12
    @Seelenvirtuose I find the question useful. I was wondering if Java had this operator, Google led me here, now I know. Plus, OP wanted to spread an array into a function arguments, and asked if it was possible. I don't think it's an XY. – Saucistophe Nov 30 '18 at 20:33

8 Answers8

29

In java there is concept of Variable Arguments, using which you can pass different numbers of arguments to same function.

I am taking your code as an example :

public class Foo {
    public int doSomething (int ...a) {
      int sum = 0;
      for (int i : a)
           sum += i;
        return sum;
    }
 }

Now you can call this function as :

doSomething (args)

For more information you can visit below link : http://www.geeksforgeeks.org/variable-arguments-varargs-in-java/

Nishesh Pratap Singh
  • 2,131
  • 2
  • 13
  • 20
27

Actually, because for compatibility reasons, the signature of a method, which is using varargs function(Object... args) is the equivalent of a method declared with an array function(Object[] args).

Therefore in order to pass and spread any collection to function which expects varargs, you need to transform it to the array:

import java.util.Arrays;
import java.util.stream.Stream;

public class MyClass {
  
  static void printMany(String ...elements) {
     Arrays.stream(elements).forEach(System.out::println);
  }
  
  public static void main(String[] args) {
    printMany("one", "two", "three");
    printMany(new String[]{"one", "two", "three"});
    printMany(Stream.of("one", "two", "three").toArray(String[]::new));
    printMany(Arrays.asList("foo", "bar", "baz").toArray(new String[3]));
  }
}

All these calls of printMany will print:

one

two

three

It's not exactly the same as spread operator, but in most cases, it's good enough.

Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
17

Java language does not provide an operator to do this, but its class library has a facility to do what you need.

[from OP's comment] The developer of Foo could choose himself the number of arguments that function doSomething takes. I would then be able to construct a "bag" of arguments and inject it in the method.

Use reflection API, this is what it is for. It requires you to package arguments in an array. There is a lot of extra work required, including wrapping/unwrapping individual method arguments, and method result, but you can check the signature at run-time, construct an array, and call the method.

class Test {
    public static int doSomething(int a, int b, int c) {
        return a + b + c;
    }
    // This variable holds method reference to doSomething
    private static Method doSomethingMethod;
    // We initialize this variable in a static initialization block
    static {
        try {
            doSomethingMethod = Test.class.getMethod("doSomething", Integer.TYPE, Integer.TYPE, Integer.TYPE);
        } catch (Exception e) {
        }
    }
    public static void main (String[] ignore) throws java.lang.Exception {
        // Note that args is Object[], not int[]
        Object[] args = new Object[] {1, 2, 3};
        // Result is also Object, not int
        Object res = doSomethingMethod.invoke(null, args);
        System.out.println(res);
    }
}

The above code prints 6 (demo).

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • How can i get the method reference without explicitly giving the argument types? I have an array containing the arguments, so i want method reference which accept those arguments without providing `Object.TYPE` in `getMethod()` – Enferno Nov 14 '20 at 14:33
  • 1
    @Enferno Java reflection library does not offer a shortcut for this. You need to get all methods by a given name, and loop through them to see which one fits the parameter list that you have. – Sergey Kalinichenko Nov 14 '20 at 14:40
3

Unfortunately, you can't do this. The spread operator works in javascript because functions are allowed to accept two few (left out arguments are undefined) or too many arguments (extra arguments are ignored) of any type. Java, being strongly and statically typed, must always know exactly how many and what kind of arguments you are passing before you even compile the code.

You can probably find a hackaround with Java 8's functional interfaces, method references and var-args, but it would require so much boilerplate that I won't even bother posting it here.

Leo Aso
  • 11,898
  • 3
  • 25
  • 46
  • 2
    yeah but what about varargs with java, spread operator could be used there –  Jan 28 '19 at 10:35
1

You could do something like below

List<Integer> numbers = Arrays.asList(1, 2,3);
doSomething(numbers.toArray(Integer[]::new));
-1
// A method that takes a variable number of integer arguments.
    static void fun(int ...a)
    {
        System.out.println("Number of arguments: " + a.length);
  
        // using for each loop to display contents of a
        for (int i: a)
            System.out.print(i + " ");
        System.out.println();
    }
  
    // Driver code
    public static void main(String args[])
    {
        // Calling the varargs method with different number
        // of parameters
        fun(100);         // one parameter
        fun(1, 2, 3, 4);  // four parameters
        fun();            // no parameter
    }

OUTPUT:

Number of arguments: 1
100 
Number of arguments: 4
1 2 3 4 
Number of arguments: 0

This is the most simple example for providing a Variable number of arguments in java. For JavaScript guyz this is also called as Rest Operator. But don't confuse with that if you know JS. To know more about this in Java please refer : [https://www.geeksforgeeks.org/variable-arguments-varargs-in-java/][1]

-2

If you need to call only few methods this way, you can do without Reflection simply by creating a wrapper class like this:

Main class (SpreadTest.java):

public class SpreadTest {
     public static void main(String []args){
        int[] a = {1, 2, 3};
        System.out.println(new FooWrapper().doSomething(a)); // 6
     } 
}

Your wrapper class (FooWrapper.java):

public class FooWrapper extends Foo {
    public int doSomething(int ...a) {
        return super.doSomething(a[0], a[1], a[2]);
    }
}

The class with the method which does the work (Foo.java):

public class Foo {
    public int doSomething(int a, int b, int c) {
        return a + b + c;
    }
}
Mikhail Batcer
  • 1,938
  • 7
  • 37
  • 57
-5

Contrary to numerous comments, e.g. by @JoeC and @Henry, and incorrect answers, this is possible in Java.

I'd like to use something like

doSomething (...args)

instead of calling doSomething(args[0], args[1], args[2]).

You need:

public int doSomething (int ... args) {
    int sum = 0;
    for (int i : args)
    {
        sum += i;
    }
    return sum;
}

I'd like not to change the implementation of such a function.

Impossible, a priori. Either you are adding exactly three arguments, or as many arguments as were actually passed. Make up your mind about this.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 3
    I think this approach is what OP hinted at when he wrote "I saw that this is possible in the declaration of functions, but I'd like not to change the implementation of such a function." – Sergey Kalinichenko Sep 24 '17 at 09:55
  • @dasblinkenlight That is a contradiction in terms. Either he wants to sum an arbitrary number of arguments, or three. Not both at the same time. – user207421 Sep 24 '17 at 09:57
  • 1
    I think he wants to sum exactly three arguments, but he wants to "package" them in a three-element array for the call. – Sergey Kalinichenko Sep 24 '17 at 09:58
  • 1
    @dasblinkenlight Or anything. It's up to him to clarify, not up to us to guess. – user207421 Sep 24 '17 at 09:59
  • 1
    It's because at call time, I don't know what is implemented on the class. The developer of `Foo` could choose himself the number of arguments that function `doSomething` takes. I would then be able to construct a "bag" of arguments and inject it in the method. Without this spread operator, I think I have to make a switch from minimum arguments taken to maximum and cover all the cases. – Hammerbot Sep 24 '17 at 10:02
  • 1
    @El_Matella So this is how you write it, and specifying that you don't want to vary the implementation from summing exactly three arguments is completely futile. Decide. We can't help you with that. You seem to have the whole thing upside down. The developer of `Foo` should be *constrainted* to this method signature, just as he was in the Javascript case. – user207421 Sep 24 '17 at 10:06
  • 1
    The OP asked for entirely different thing (if not opposite): not for variable number of argments in method *definition*, but for spreading a collection in method *calling*. – Mikhail Batcer Nov 30 '17 at 21:56
  • 1
    There is a question "how to pass params when you don't know how many of them " – Tioma Mar 16 '18 at 08:53
  • @Tioma There is indeed, and this is the answer to it. Your point? – user207421 Mar 16 '18 at 09:20
  • @MikhailBatcer I have answered the question. Your point eludes me. – user207421 Mar 16 '18 at 09:22
  • 2
    @EJP No, you didn't. And you admitted it yourself in your answer. First, you wrote: "this is possible in Java", referring to the question. But in the end you concluded: "Impossible". – Mikhail Batcer Mar 17 '18 at 17:18
  • @MikhailBatcer I said that doing it *without modifying the implementation* is impossible. Don't misquote me, and don't fabricate contradictions where they don't exist. It isn't respectable. – user207421 Apr 07 '18 at 10:57
  • 1
    This doesn't answer the question sorry, we need to able to spread the values of a list as pass the elements of the list as arguments, for example: https://gist.github.com/ORESoftware/3c4f25de70e9d1849572752c4b59d1a9 – Alexander Mills Feb 15 '19 at 05:53