6

The following Python snippet does exactly what I mean:

def function(a, b, c):
    print("%i :: %s :: %f" % (a,b,c))

a = 1
b = "abc"
c = 1.0

function(a, b, c)

list = [a, b, c]

# This is what I am searching for in Java
function(*(list))

So I have one list-like structure and I don't know how many arguments it has, but I know that it has the right number of arguments with the right type and format. And I want to pass them to a method. So I have to "expand" those arguments. Does anybody know how to do so in Java?

ndmeiri
  • 4,979
  • 12
  • 37
  • 45
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
  • Are you perhaps looking [for](http://stackoverflow.com/questions/766559/when-do-you-use-varargs-in-java) [varargs](http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html)? – user Feb 16 '12 at 10:27
  • Define "expand"? It's not clear from your example what this means. – Tudor Feb 16 '12 at 10:30
  • If you just want to use something like the `%` operator, you can use the `Formatter` class as in my answer below. –  Feb 16 '12 at 10:39

6 Answers6

5

Java doesn't have this facility, since as a statically, strongly-typed language it's uncommon for a method to take a collection of values of uniform type that could all be stored in some composite object (array, list, etc.). Python doesn't have this problem because everything is dynamically-typed. You can do this in Java by defining a helper method that takes a single object in holding all the parameters, then expands them out in a call to the real method.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
4

You could declare optional parameters with Object... such that you can pass an arbitrary number of arguments and then cast each of them to the required type:

public void method1(String p1, Integer p2) {
}

public void method2(Object... params) {
    method1((String)params[0], (Integer)params[1];
}

You can call method2 as:

method2("abc", 1);

Of course, this example is a bit redundant, because you could directly call method1, but that's not what I wanted to illustrate. :)

Tudor
  • 61,523
  • 12
  • 102
  • 142
1

You can't. In Java, method parameters are checked at compile time, and since the content of the array is only defined at runtime, this check would be impossible.

Your closest solution is to have a method taking an array as a parameter, and eventually expanding the arguments to place a call to the targeted method.

Vivien Barousse
  • 20,555
  • 2
  • 63
  • 64
0

You can do this:

import java.util.Formatter;

// ...

StringBuilder target = new StringBuilder();
Formatter formatter = new Formatter(target);

List<Integer> integers = new ArrayList<Integer>();
integers.add(1);
integers.add(2);
integers.add(3);

formatter.format("%d %d %d", integers.toArray());
System.out.println(target.toString());

Edit: Javadoc for java.util.Formatter

0

I think this does the same thing as your initial code with a similar syntax...

public class MultiArgs {

    public static void main(String[] args) {
        int a = 1;
        String b = "abc";
        double c = 1.0;
        List<?> list = Arrays.asList(a, b, c);

        System.out.println(function(a, b, c)); // 1 :: abc :: 1.000000
        System.out.println(function(list.toArray())); // 1 :: abc :: 1.000000
    }

    private static <T extends Object> String function(T... objects) {
        return String.format("%d :: %s :: %f", objects);
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
0

It's not recommended (as templatetypedef's answer notes, this is a very uncommon pattern for a strongly-typed language), but you could do what you want using reflection.

SomeType foo = //obtained from elsewhere
List<?> args = //obtained from somewhere else

//this bit obtains the method handle
Method theMethod = null;
for (Method m : foo.getClass().getMethods()) {
    if (m.getName().equals("theMethodIWant") {
        theMethod = m;
        break;
    }
}

theMethod.invoke(foo, args.toArray()); //this is the line that does the magic

Some fairly serious caveats:

  • Reflection can have a significant performance impact compared to a direct call — much more so than in a dynamic language like python. This may or may not matter. When in doubt, measure.

  • The block that obtains a method handle will result in a null pointer if a method with this name isn't found

  • The list of arguments must have exactly the right number of arguments, with the right type. You are responsible for verifying this somewhere else. This is harder than it would be in a dynamic language, as the runtime will do a lot less (if any) type coercion for you.

  • method.invoke() can throw a host of exceptions (IllegalAccessException, IllegalArgumentException, InvocationTargetException, etc) that this example ignores. You'll have to catch and deal with those errors appropriately

  • if the method being called isn't public, you'll have to do even more work than this to call it.

  • this example doesn't even try to deal with the case where you have an overloaded method. Two methods named "theMethodIWant" with different argument lists will require a different approach to obtain the method handle.

All in all, I would recommend not using reflection if possible.

Sean Reilly
  • 21,526
  • 4
  • 48
  • 62