'How to implement a method with an unknown number of arguments?
I have 1 interface and 3 class. I would like the class to be able to both implement the interface which need a transform method. This method must exist but there can't be more than one per class. I don't 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 doesn't work. But I hope you got the idea. Is something like this possible in java ? How should I call them in a generic way ? Let's say if I have an other method like :
void callTransf(A a, Object ... objs){
Method m = a.getClass().getMethods()[0];
m.invoke(a, objs)
}
Solution 1:[1]
A practicable solution would be to declare the interface as a generic one:
public interface Transformation<S, R> {
R transform(S source);
}
The type parameter S plays the source role; the type parameter R plays the result role.
You now can create source and result classes for each different transformation. An 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 that you declare the transformation as following:
public final class TransformationA implements Transformation<TransformationSourceForA, TransformationResultForA> {
@Override
public TransformationResultForA transform(TransformationSourceForA source) { ... }
}
The principle is to delegate the needs for different fields to a class and not to the method's parameter.
Solution 2:[2]
What you are asking isn't possible. If interface method uses Varargs then others must too. So one solution would be to have both classes use this interface. Here is 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 are calling method in B then you must convert string to char array:
String str = "example";
char[] charArray = str.toCharArray();
When calling method in A you make sure to convert integer to char:
int i = 5;
transform((char)Character.forDigit(i, 10), 'a'); // 10 stands for number radix which is probably 10
This isn't perfect solution but it is working one.
But a bit simpler solution without varargs is using just char array, but again you need to convert inputs to 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]);
}
}
Anyway you do it, you will end up with a bit complicated code, even if using generics you must remember that 1 method takes 1 parameter and other one 2. I actually think that it would be best to simply make this methods separate.
Solution 3:[3]
It's a very old question but I don't see a correct implementation in any of the solution. OP was going the right way and is the correct implementation but needs to be written like this -
public interface A<T>{
public T transform(Object ... args);
}
public class B implements A{
public void transform(Object ... args){
System.out.println((String)args[0]);
}
}
public class C implements A{
public void transform(Object ... args){
Integer a = (Integer)args[0];
Integer b = (Integer)args[1];
System.out.println(a+b);
}
}
public static void main(String [] vals){
//Interface A
A b = new B();
A c = new C();
b.transform("Hello");
c.transform(new Integer(1), 'c');
}
You will see it's importance if you use Spring or other DI framework then all you need to do is
@Inject
@Qualifier("B") // For Implementation class B
A b;
@Inject
@Qualifier("C") // For Implementation class C
A C
I see accepted answer is very convuluted and in the end, it is just directly calling the implementation class -
Ex:TransformerB b = new TransformerB;
b.transform();
What's the point of creating all the interfaces???
Solution 4:[4]
this is a very interesting Question.you can use method overloading concept if you know the maximum number of arguments coming.
lets say you know that at max user 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 dont know the maximum number of args you can implement in multiple ways. 1.create a collection and store them in collection object and pass the object as argument.
List args= new List();
l.add(arg1)
----------
----------
----------
l.add(argn)
now pass this as argument to the function call as
objecReference.implementation(l)
2.using var arg methods. this is the very easy way to solve this kind of 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)
Solution 5:[5]
As per your requirement you want to override method from your interface in class B and C , but you cannot do the way you have done that.
One way to do is as :
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) {
}
}
Solution 6:[6]
One possible solution could be to use Marker Interfaces. A marker (or tagging) interface is an interface that has no methods or constants inside it. It provides run-time type information about objects.
Here is an example that uses Input interface as transform method parameter. An instance of a class that implements this marker interface can be used as transform method argument.
public interface Input {
}
public interface Transformable {
void transform(Input input);
}
public class InputForA implements Input {
int a;
String b;
public int getA() {
return a;
}
public InputForA setA(int a) {
this.a = a;
return this;
}
public String getB() {
return b;
}
public InputForA setB(String b) {
this.b = b;
return this;
}
}
public class TransformerA implements Transformable {
@Override
public void transform(Input input) {
InputForA inputForA = (InputForA) input;
System.out.println(inputForA.getA() + inputForA.getB());
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Seelenvirtuose |
| Solution 2 | |
| Solution 3 | Dinesh Arora |
| Solution 4 | D Sai Krishna |
| Solution 5 | Kohei TAMURA |
| Solution 6 | Mohsen Zamani |
