简体   繁体   中英

In Java, can I specify any amount of generic type parameters?

I am looking to create a particular type of interface in Java (although this is just as applicable to regular classes) . This interface would need to contain some method, say, invoke ; it would be called with a varying amount of parameters depending on the generic type arguments supplied.

As an example:

public interface Foo<T...> {
    public void invoke(T... args);
}

// In some other class
public static Foo<Float, String, Integer> bar = new Foo<Float, String, Integer>() {
    @Override
    public void invoke(Float arg1, String arg2, Integer arg3) {
        // Do whatever
    }
};

To explain, briefly, how this could be used (and provide some context) , consider a class Delegator : the class takes a varying number of generic types, and has a single method - invoke , with these parameter types. The method passes on its parameters to an object in a list: an instance of IDelegate , which takes the same generic types. This allows Delegator to choose between several delegate methods (defined inside IDelegate ) without having to create a new class for each specific list of parameter types.

Is anything like this available? I have read about variadic templates in C++, but cannot find anything similar in Java. Is any such thing available? If not, what would be the cleanest way to emulate the same data model?

Is anything like this available? I have read about variadic templates in C++, but cannot find anything similar in Java. Is any such thing available?

No, this feature is not available in Java.

No, there is nothing like that directly available. However if you use a library with Tuple classes you can simulate it by just making the interface

interface Foo<T> {
    void invoke(T t);
}

(This interface is essentially the same as Consumer<T> .)

Then you could do for example

Foo<Tuple<String, Integer, Date, Long>> foo = new Foo<>() {
    ...
}

You would need a separate Tuple type for each number of parameters. If you have a Tuple class for 4 parameters, but not one for 5, you could squeeze an extra parameter in by using a Pair class.

Foo<Tuple<String, Integer, Date, Pair<Long, BigDecimal>>> foo = ...

By nesting tuple types in this way you get an unlimited number of parameters. However, these workarounds are really ugly, and I would not use them.

Given the context you provided I would recommend using a List as a parameter. If these parameters have something in common, you can restrain your list to <T extends CommonParrent> instead of using List<Object> . If not, you may still want to use marker interface.

Here is an example.

public class Main {

    public static void main(String[] args) {
        delegate(asList(new ChildOne(1), new ChildTwo(5), new ChildOne(15)));
    }

    private static <T extends Parent> void delegate(List<T> list) {
        list.forEach(item -> {
            switch (item.type) {
                case ONE: delegateOne((ChildOne) item); break;
                case TWO: delegateTwo((ChildTwo) item); break;
                default: throw new UnsupportedOperationException("Type not supported: " + item.type);
            }
        });
    }

    private static void delegateOne(ChildOne childOne) {
        System.out.println("child one: x=" + childOne.x);
    }

    private static void delegateTwo(ChildTwo childTwo) {
        System.out.println("child two: abc=" + childTwo.abc);
    }

}

public class Parent {
    public final Type type;

    public Parent(Type type) {
        this.type = type;
    }
}

public enum Type {
    ONE, TWO
}

public class ChildOne extends Parent {
    public final int x;

    public ChildOne(int x) {
        super(Type.ONE);
        this.x = x;
    }
}

public class ChildTwo extends Parent {
    public final int abc;

    public ChildTwo(int abc) {
        super(Type.TWO);
        this.abc = abc;
    }
}

The biggest flaw of this solution is that children have to specify their type via enum which should correspond to the casts in the switch statement, so whenever you change one of these two places, you will have to remember to change the other, because compiler will not tell you this. You will only find such mistake by running the code and executing specific branch so test driven development recommended.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM