Java Variable Number of Arguments for a Method

How can I create a Java method that accepts a variable number of arguments?

You could write a convenience method:

public PrintStream print(String format, Object... arguments) {
return System.out.format(format, arguments);
}

But as you can see, you've simply just renamed format (or printf).

Here's how you could use it:

private void printScores(Player... players) {
for (int i = 0; i < players.length; ++i) {
Player player = players[i];
String name = player.getName();
int score = player.getScore();
// Print name and score followed by a newline
System.out.format("%s: %d%n", name, score);
}
}

// Print a single player, 3 players, and all players
printScores(player1);
System.out.println();
printScores(player2, player3, player4);
System.out.println();
printScores(playersArray);

// Output
Abe: 11

Bob: 22
Cal: 33
Dan: 44

Abe: 11
Bob: 22
Cal: 33
Dan: 44

Note there's also the similar System.out.printf method that behaves the same way, but if you peek at the implementation, printf just calls format, so you might as well use format directly.

  • Varargs
  • PrintStream#format(String format, Object... args)
  • PrintStream#printf(String format, Object... args)

How to specify variable number of arguments using jni

This should works:

public native void callLuaFunctionWithParams(String functionName, int numParams, Object... params);

Variable number of arguments in generic class in Java for callback function

String, Integer, String is three parameters while your GenericRunnable works with one.

Wrap them up into a class

class ABC {
private String a;
}

and use that class as a generic parameter.

class MyRunnable extends GenericRunnable<ABC> {
@Override
public void callback(ABC request) {
String a = request.getA();
...
}
}

Generate a call to a method with a variable number of arguments

In order to avoid mixing Scala's interpolation and JavaPoet's templating it looks like the best way to achieve this is by making a CodeBlock that contains the parameter list and embedding it in the code with a template, as in the following example:

val params = fields.map { case (name, _) => CodeBlock.of(name) }
val paramList = CodeBlock.join(params.asJava)
method.addStatement("return new $T($L)", className, paramList)

The template here makes use of the $L placeholder that will be replaced by a literal (in this case, the assembled list of parameters).

How to implement a method with an unknown number of arguments?

You can achieve what you want, with some changes and some help of functional programming...

TL;DR

The main idea is that the transform method doesn't receive any arguments. Instead, it will return an instance of some functional interface.

The implementation of this functional interface will consist of the code that would have been executed by the transform method if it had arguments.

To represent arguments of different types and/or a different number of arguments for each subclass of the A interface, we'll use covariance in the return type of the method transform.

This means that the functional interface will be generic (so that the type of the arguments can be different for each subclass of A), and that there will be subinterfaces that will extend this functional interface, each one accepting a different number of arguments in its single abstract method. This will allow the transform() method's return value to have either 1, 2, 3, ... etc arguments.

To execute the code returned by the transform() method, we'll do:

instanceOfB.transform().execute("hello");
instanceOfC.transform().execute(1, 'a');
instanceOfD.transform().execute(1, "hello");

Finally, in order to be able to execute the code in a generic way, the base functional interface defines a varargs method executeVariadic(Object... args), which will be implemented as a default method by every child functional interface, delegating to its execute method and casting the arguments as needed.


Now the long version...

Let's start by renaming your A interface to something more descriptive. As it defines a method called transform, let's name it Transformer.

Then, let's create a functional interface that will represent the transform method of the Transformer interface. Here it is:

@FunctionalInterface
public interface Transformation {

void executeVariadic(Object... args);
}

This interface just defines one single abstract method (SAM) that receives an Object... varargs argument. It is there so that subinterfaces can override it.

Now, let's create a Transformation1 functional interface that extends the Transformation interface:

@FunctionalInterface
public interface Transformation1<A> extends Transformation {

void execute(A a);

@Override
@SuppressWarnings("unchecked")
default void executeVariadic(Object... args) {
this.execute((A) args[0]);
}
}

This Transformation1<A> functional interface is generic and defines the single abstract method execute, which takes one argument of type A. The executeVariadic method is overriden as a default method that delegates its execution to the execute method, casting the first argument accordingly. This cast generates a warning, but oh, well... we'd better learn to live with it.

Now, let's create an analogous interface with two generic type parameters and an execute method that receives two arguments whose types match the generic type parameters:

@FunctionalInterface
public interface Transformation2<A, B> extends Transformation {

void execute(A a, B b);

@Override
@SuppressWarnings("unchecked")
default void executeVariadic(Object... args) {
this.execute((A) args[0], (B) args[1]);
}
}

The idea is the same: the Transformation2 interface extends the Transformation interface and we override the executeVariadic method so that it is delegated to the execute method, casting the arguments accordingly (and suppressing the annoying warning).

For completeness, let's introduce the Transformation3 interface, which is analogous to the previous TransformationX ones:

@FunctionalInterface
public interface Transformation3<A, B, C> extends Transformation {

void execute(A a, B b, C c);

@Override
@SuppressWarnings("unchecked")
default void executeVariadic(Object... args) {
this.execute((A) args[0], (B) args[1], (C) args[2]);
}
}

Hope the pattern is clear by now. You should create as many TransformationX interfaces as arguments you want to support for the transform method of your Transformer interface (A interface in your question, remember I've renamed it).

So far so good, I know this answer is long, but I needed to define the interfaces above so that they can now be used to put all the pieces together.

Remember your A interface? Let's not only keep its name changed to Transformer, but also the signature of its transform method:

@FunctionalInterface
public interface Transformer {

Transformation transform();
}

So this is your base interface now. The transform method no longer has arguments, but returns a Transformation instead.

Let's see how to implement your B, C and D classes now. But first, allow me to rename them to TransformerB, TransformerC and TransformerD, respectively.

Here's TransformerB:

public class TransformerB implements Transformer {

@Override
public Transformation1<String> transform() {
return a -> System.out.println(a); // or System.out::println
}
}

The important thing here is the use of covariance in the return type of the transform method. And I'm using the Transformation1<String> type, which is a subtype of Transformation and indicates that, for the TransformerB class, the transform method returns a transformation that accepts one argument of type String. As the Transformation1 interface is a SAM type, I'm using a lambda expression to implement it.

Here's how to invoke the code inside the TransformerB.transform method:

TransformerB b = new TransformerB();
b.transform().execute("hello");

b.transform() returns an instance of Transformation1, whose execute method is immediately invoked with the String argument it expects.

Now let's see the implementation of TransformerC:

public class TransformerC implements Transformer {

@Override
public Transformation2<Integer, Character> transform() {
return (a, b) -> System.out.println(a + b);
}
}

Again, covariance in the return type of the transform method allows us to return a concrete Transformation, in this case Transformation2<Integer, Character>.

Usage:

TransformerC c = new TransformerC();
c.transform().execute(1, 'A');

For the TransformerD example, I've used a three-argument transformation:

public class TransformerD implements Transformer {

public Transformation3<Integer, Double, String> transform() {
return (a, b, c) -> System.out.println(a + b + c);
}
}

Usage:

TransformerD d = new TransformerD();
d.transform().execute(12, 2.22, "goodbye");

This is all type-safe, because the generic types can be specified in the TransformationX return type of each concrete transform method implementation. It's not possible to use primitive types, though, because primitive types cannot be used as generic type parameters.


Regarding how to call the transform method in a generic way, it's straightforward:

void callTransf(Transformer a, Object... args) {
a.transform().executeVariadic(args);
}

This is why the executeVariadic method exists. And it's overriden in each TransformationX interface, so that it can be used polymorphically, as in the code above.

Calling the callTransf method is straightforward too:

callTransf(b, "hello");
callTransf(c, 1, 'A');
callTransf(d, 12, 2.22, "goodbye");


Related Topics



Leave a reply



Submit