0

Is there a way in Java to create a method that would return the list of parameters of another method such that I am able to call

anotherMethod(method())

where anotherMethod has arbitrary arguments like

public void anotherMethod(int a, int b, String c)

And what is if the types stay the same, like with

public int add(int a, int b, int c)

If there is no such way, how could I model the list of parameters such that it would work? Is it a List or an array or something else?

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
ecdhe
  • 421
  • 4
  • 17

4 Answers4

2

If the number of parameters is fixed at the call site, you could use varargs

int add(int... numbers)

otherwise you'd use an array or collection

int add(int[] numbers)

You can then of course have another method provide the value of these parameters:

add(someOtherMethod())
Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    what would be the return type of someOtherMethod() in the present case of int add(int a, int b)? – ecdhe Dec 04 '17 at 23:36
  • For `add(int a, int b)` you need two parameters.There is no way to write it like `add(someOtherMethod())`, because a method can only return a single value. You need to have your method accept an array, collection or varargs (the latter are basically just an array). – Thilo Dec 04 '17 at 23:40
  • I see, thanks for the reply. One thing more to add, there is a typo in your answer, the varargs is for an arbitrary number of params not a fixed number. – ecdhe Dec 04 '17 at 23:43
  • I meant to say that the number is fixed (known at compile-time) for each call site, so that you can write `add(a,b,c)` --- for which you need to know that you have three parameters. But each call site can still have a different number than other call sites (i.e. there can also be `add(a,b,c,d)` elsewhere). If the caller does not know how many parameters there are, you have to pass in an array or a collection. – Thilo Dec 05 '17 at 03:29
1

Varargs

Java has a built-in feature to denote a variable length of arguments. It is called varargs (documentation) (variable arguments) and it only works if the type stays the same. The syntax for a method is like this:

public int add(int... values)

Note the int... values which denotes varargs. A caller can now call the method like

add(null)        // Passing null
add(values)      // Passing an int[]
add()            // No arguments
add(a)           // One int
add(a, b)        // Two ints
add(a, b, c)     // Three ints
add(a, b, c, d)  // Four ints
...

Note the three special cases null, int[] and empty.

What Java does is it will convert the arguments into an array. So inside the method values will be a regular int[]. You could thus implement the method like

public int add(int... values) {
    int sum = 0;
    for (int value : values) {
        sum += value;
    }
    return sum;
}

If you, as a caller, want to pass the return value of a function you just need to make sure that it returns an array like int[]. So the following would work:

public int[] valueProvider() {
    int[] values = ...

    return values;
}

and then call it like

int sum = add(valueProvider());

Collection, Iterable and Stream

Besides that, if you don't want to use varargs or arrays, you can use Collections (documentation). A collection may be a List or a Set and so on. For example you could declare

public int add(Collection<Integer> values)

and feed it like

Collection<Integer> values = new ArrayList<>();
values.add(1);
values.add(2);
int sum = add(values);

An Iterable<Integer>, in contrast to Collection<Integer> would even be more flexible.

Using a Stream (documentation) would also work like a charm and is probably one of the most flexible variants since the source of a stream could be anything and nearly anything of the standard library supports a stream representation.


Changing type

Now note that what you searched for in the beginning, a method that is able to feed arbitrary arguments, is not possible in Java.

The main problem is that the types may change, so you may have a method like

public void doSomething(int first, String second, File third)

and you won't be able to feed the method with varargs, Collections or any of the presented methods.

In that case you will need a wrapper class like

public class DoSomethingArguments {
    private int mFirst;
    private String mSecond;
    private File mThird;

    public DoSomethingArguments(int first; String second, File third) {
        this.mFirst = first;
        this.mSecond = second;
        this.mThird = third;
    }

    // Some getters
}

(or a generic tuple class, a triple in this case)

But then you would need to change the method to

public void doSomething(DoSomethingArguments arguments)

what is probably not what you wanted since you probably intended to not change the signature of doSomething.

But unfortunately there is no way to feed a method like this in such a way.

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
1

There is nothing that works the way you wish for at compile time. As the other answers are pointing out, there are varargs. But that is just syntactical sugar. That is just the compiler implicitly creating an array of a certain type for you.

But beyond that, there is reflection. Reflection allows you to dynamically inspect classes and methods at *runtime.

In other words: you can do something like

Object whatever = ...
Class<?> someClass = whatever.getClass();

And now you can ask someClass about the methods it has. And which parameters they need.

But as said: all of that is runtime only. And it the reflection APIs are very easy to get wrong. And you only find out at runtime, when some exception is thrown.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
0

There is not direct way to pass multiple values in the way you want. But you can use a indirect way to pass a group of values of different type. I can think of two ways but their can be more.

Firs - Use a map, just insert the values you want to pass in the collection and pass the collection to the second method.

Second - Create a bean (Java POJO) to pass as parameter to the consuming method.

A small sample code.

 class Sample{
    private int a;
    private String b;
    private int c;

    Sample(int a,String b,int c){
        this.a = a;
        this.b = b;
        this.c = c;
    }
    public int getA() {
        return a;
    }
    public void setA(int a) {
        this.a = a;
    }
    public String getB() {
        return b;
    }
    public void setB(String b) {
        this.b = b;
    }
    public int getC() {
        return c;
    }
    public void setC(int c) {
        this.c = c;
    }
}
    public class PassingExample {
    public void consumerofInputs (Map<Integer, Object> input)/*(int a, String b, int c)*/{
        System.out.println("I use three different inputs : int, string and int");
        for (Map.Entry<Integer, Object> entry : input.entrySet()) {
            System.out.println("Key : " + entry.getKey() + " Value : " + entry.getValue());
        }

    }

    public Map producingInput() {
        Map<Integer, Object> input = new HashMap<Integer, Object>();
        input.put(1, 10);
        input.put(2, "input");
        input.put(3, 89);
        return input;
    }

    public Sample createClassAsInput(){
        Sample input = new Sample(10,"class-input",30);
        return input;

    }

    public void useSampleAsInput(Sample input){
        System.out.println("\nUsing Class as input \nInt::"+input.getA()+"\nString::"+input.getB()+"\nInt::"+input.getC());
    }

    public static void main(String[] args) {
        PassingExample example = new PassingExample();
        example.consumerofInputs(example.producingInput());
        example.useSampleAsInput(example.createClassAsInput());
    }
} 
SK -
  • 459
  • 5
  • 15