16

Here's another question of "How would I do this in Java?" In Python, I can use the '*' symbol to unpack arguments like so:

>>> range(3, 6)             # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            # call with arguments unpacked from a list
[3, 4, 5]

Java supports getting a list of args with ...args syntax, but is there a way (perhaps using the Reflection libraries?) to unpack those for some other function?

Kevin
  • 175
  • 1
  • 1
  • 5
  • 2
    Can you explain what the python code does? – Kaj May 19 '11 at 17:43
  • Maybe you could explain what you're trying to accomplish. It doesn't sound very Java-like as written... – Jonathon Faust May 19 '11 at 17:55
  • 1
    @Kaj Sure. range() is a Python function that takes two integer arguments and produces an array of each integer between them, excluding the upper bound. The `*args` syntax takes the already-defined array `args` and expands / unpacks it into two distinct function arguments. It's like calling `range(args[0], args[1])`. – Kevin May 19 '11 at 17:56

2 Answers2

13
public void printStrings(String... strings)
{
   // the strings parameter is really a String[].
   // You could do anything to it that you normally
   // do with an array.
   for(String s : strings){
      System.out.println(s);
   }
}

Can be called like this:

String[] stringArray = new String[10];
for(int i=0; i < stringArray.length; i++){
   stringArray[i] = "String number " + (i+1);
}

printStrings(stringArray);

The ... syntax is really syntactic sugar for arrays.

Java doesn't have the facility that you describe, but you could fake it several ways.

I think the closest approximation means overloading any function that you want to use in that fashion using varargs.

If you have some method:

public void foo(int a, String b, Widget c) { ... }

You can overload it:

public void foo(Object... args) {
    foo((Integer)args[0], (String)args[1], (Widget)args[2]);
}

But this is really clumsy and error prone and hard to maintain.

More generically, you could use reflection to call any method using any arguments, but it's got a ton of pitfalls, too. Here's a buggy, incomplete example of how it gets ugly really fast:

public void call(Object targetInstance, String methodName, Object... args) {
    Class<?>[] pTypes = new Class<?>[args.length];
    for(int i=0; i < args.length; i++) {
        pTypes[i] = args[i].getClass();
    }
    Method targetMethod = targetInstance.getClass()
              .getMethod(methodName, pTypes);
    targetMethod.invoke(targetInstance, args);
}
Jonathon Faust
  • 12,396
  • 4
  • 50
  • 63
  • Indeed, but the OP is interested on whether you can do this for non-variadic methods. – Oliver Charlesworth May 19 '11 at 17:47
  • @Oli what do you mean? It's not clear what he means by "unpack those for some other function." – Jonathon Faust May 19 '11 at 17:49
  • I believe he means "is there a way to programatically access (e.g. iterate over) the parameter values for the current method?" – Oliver Charlesworth May 19 '11 at 17:50
  • @Oli hmm...wouldn't that just be by name? – Jonathon Faust May 19 '11 at 17:53
  • In a generic sense. i.e. some construct that will always give you `args[]`, where `args[0]` refers to the first argument of the method, etc. – Oliver Charlesworth May 19 '11 at 17:55
  • That "the `...` syntax is really syntactic sugar for arrays" isn't really relevant -- the Python `*` syntax that OP's drawing a comparison to is also 'really syntactic sugar for tuples.' The meat of the question is just whether it's possible to unpack an iterable in Java –  Apr 07 '19 at 18:41
1

If the function you're calling is not a varargs function (declared with ...) then you do need to use reflection. Method.invoke() takes an array Object[] of arguments.

The tough part via reflection is finding the right method (well, it's easy if there's only one method with the same name, otherwise it's very difficult).

The cost is the extra time to lookup/invoke the method.


Of course, if you know the specific method at compile-time, then you can deal with this manually, e.g. here for a 3-argument function:

Object[] args = /* get args from somewhere */
callSomeNonVarargsFunction((Cast1)args[0], (Cast1)args[1], (Cast1)args[2]);
Jason S
  • 184,598
  • 164
  • 608
  • 970