Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java unpacking argument lists

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?

like image 706
Kevin Avatar asked May 19 '11 17:05

Kevin


2 Answers

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);
}
like image 105
Jonathon Faust Avatar answered Nov 15 '22 17:11

Jonathon Faust


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]);
like image 33
Jason S Avatar answered Nov 15 '22 17:11

Jason S