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

I have 1 interface and 3 classes. I would like the class to be able to implement an interface that needs a transform method. This method must exist, but cannot be more than one class. I do not know the number of parameters taken by this class.

Example:

 public interface A{ public void transform(Object ... args); } public class B implements A{ public void transform(String a){ System.out.println(a); } } public class C implements A{ public void transform(Integer a, Character b){ System.out.println(a+b); } } // super generic case if possible with Objects + primitive public class D implements A{ public void transform(int a, String b){ System.out.println(a+b); } } 

This does not work. But I hope you have an idea. Is there something similar in Java? How can I call them common? Let's say if I have another method like:

 void callTransf(A a, Object ... objs){ Method m = a.getClass().getMethods()[0]; m.invoke(a, objs) } 
+7
java java-8 interface variadic-functions
source share
5 answers

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

TL; DR

The basic idea is that the transform method does not receive any arguments. Instead, it will return an instance of some functional interface.

The implementation of this functional interface will consist of code that would be 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 interface A we will use covariance in the return type of the transform method.

This means that the functional interface will be common (so that the type of arguments can be different for each subclass of A ) and that there will be subinterfaces that extend this functional interface, each of which will take a different number of arguments in one abstract method. This will allow you to return the value of the transform() method for arguments 1, 2, 3, ... etc.

To execute the code returned by the transform() method, we will do the following:

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

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


Now the long version ...

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

Then we create a functional interface, which will be a transform method of the Transformer interface. Here he is:

 @FunctionalInterface public interface Transformation { void executeVariadic(Object... args); } 

This interface defines only one abstract method (SAM) that takes an argument of Object... varargs. It exists, so subinterfaces can override it.

Now create the Transformation1 functional interface, which 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 a single abstract execute method that takes a single argument of type A The executeVariadic method executeVariadic overridden as the default method, which delegates its execution to the execute method, appropriately applying the first argument. This throw gives rise to a warning, but oh, well ... we better learn to live with it.

Now we will create a similar interface with two parameters of the general type and execute , which will receive two arguments whose types correspond to the parameters of the type type:

 @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 executeVariadic that it is delegated to the execute method, appropriately discarding the arguments (and suppressing the annoying warning).

For completeness, we introduce the Transformation3 interface, which is similar to the previous TransformationX units:

 @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 sample is already clear. You should create as many TransformationX interfaces as possible as arguments that you want to support for the transform method of your Transformer ( A ) interface in your question, remember that I renamed it).

So far so good, I know this answer is long, but I needed to define the interfaces above so that now I could use them to connect all the parts.

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

 @FunctionalInterface public interface Transformer { Transformation transform(); } 

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

See how to implement classes B , C and D But first let me 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 } } 

Important here is the use of covariance in the return type of the transform method. And I use the type Transformation1<String> , which is a subtype of Transformation and indicates that for the TransformerB class, the transform method returns a transformation that takes a single argument of type String . Since the Transformation1 interface is a SAM type, I use a lambda expression to implement it.

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

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

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

Now consider 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 you to return a specific Transformation , in this case Transformation2<Integer, Character> .

Using:

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

In the TransformerD example, I used a transformation with three arguments:

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

Using:

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

This is all type safe because generic types can be specified in the TransformationX return type of each particular transform implementation method. However, primitive types cannot be used because primitive types cannot be used as parameters of a type type.


As for the way the transform method is invoked in a general way, this is simple:

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

This is why the executeVariadic method executeVariadic . And it is redefined in every TransformationX interface, so it can be used polymorphically, as in the code above.

Calling the callTransf method callTransf also simple:

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

A practical solution would be to declare the interface as generic:

 public interface Transformation<S, R> { R transform(S source); } 

A parameter of type S plays the role of a source; a parameter of type R plays the role of the result.

Now you can create source and summary classes for each other transformation. Example:

 public final class TransformationSourceForA { // Here you declare whatever fields and methods you need for an A source. // For example: int a; String b; } public final class TransformationResultForA { // Here you declare whatever fields and methods you need for an A result. } 

With this, you declare the conversion as follows:

 public final class TransformationA implements Transformation<TransformationSourceForA, TransformationResultForA> { @Override public TransformationResultForA transform(TransformationSourceForA source) { ... } } 

The principle is to delegate needs for different fields to a class, not to a method parameter.

+10
source share

What you ask for is impossible. If the interface method uses Varargs, then others should. Thus, one solution would be to use both classes with this interface. Here is a general idea:

 public interface A{ public void transform(char ... args); } public class B implements A{ public void transform(char ... args){ String s = ""; for(char c : args){ s += c; } System.out.println(s); } } public class C implements A{ public void transform(char ... args){ System.out.println(args[0] + args[1]); } } 

Now, when you call the method in B, you must convert the string to a char array:

 String str = "example"; char[] charArray = str.toCharArray(); 

When calling the method in, you must convert the integer to char:

 int i = 5; transform((char)Character.forDigit(i, 10), 'a'); // 10 stands for number radix which is probably 10 

This is not an ideal solution, but it works.

But a slightly simpler solution without varargs uses only a char array, but again you need to convert the inputs to a char array.

 public interface A{ public void transform(char[]); } public class B implements A{ public void transform(char[] args){ String s = ""; for(char c : args){ s += c; } System.out.println(s); } } public class C implements A{ public void transform(char[] args){ System.out.println(args[0] + args[1]); } } 

In any case, you will do this, you will have a little complicated code, even if you use generics, you should remember that 1 method takes 1 parameter and another 2. I really think that it would be better to just make these methods separate.

+1
source share

This is a very interesting question. You can use the concept of method overloading if you know the maximum number of arguments.

will say that you know that with the maximum user it can give 2 parameters, then you can do something like this.

 public void implementation(){ System.out.println("method with zero args") } public void implementation(String arg1){ System.out.println("method with one args and is:-"+arg1) } public void implementation(String arg1,String arg2){ System.out.println("method with two args and are :-"+arg1+" "+arg2) } 

if you do not know the maximum number of arguments that you can implement in several ways. 1.Create a collection and save it in the collection object and pass the object as an argument.

 List args= new List(); l.add(arg1) ---------- ---------- ---------- l.add(argn) 

now pass this as an argument to the function call as

 objecReference.implementation(l) 

2. using var arg methods. this is a very easy way to solve such problems from java 1.8.

in implementation

 public String implementation(int(change to required datattype)...x){ //here x will act like an array for(int a:x){//iam assuming int values are coming System.out.println(a) } } 

now you can call this function with atleast 0 args like

  objecReference.implementation() objecReference.implementation(10) objecReference.implementation(10,20) objecReference.implementation(12,23,34,5,6) 
0
source share

According to your requirement, you want to override the method from your interface in classes B and C, but you cannot do the way you did it.

One way to do this:

 public interface A<T> { public void transform(T ... args); } public class B implements A<String> { @Override public void transform(String... args) { } } public class C implements A<Integer> { @Override public void transform(Integer... args) { } } 
0
source share

All Articles