Reputation: 9393
Such as in .Net, which provides several implementations of the Action
delegate (equivalent to Java Consumer
functional interface) with different number and type of arguments, I was expecting that Java 8 provides some way of specifying a Consumer
with more than one argument of different types.
I know that in Java we cannot define different types with the same name that just differ in the generic type parameters, but there would be nice fluent alternatives to provide a multi-argument Consumer
.
Is there any easy way to do it, which does not require defining a new functional interface?
Upvotes: 66
Views: 57508
Reputation: 1369
You can simply use a list:
Consumer<List> myFunction = (myListOfParameters) -> {
//do what you want with the values in your list now
myListOfParameters.forEach(System.out::println);
//types of variables
for(Object param : myListOfParameters){
System.out.println("type of the param: "+param.getClass());
}
//Access by index
System.out.println(myListOfParameters.get(0));
System.out.println(myListOfParameters.get(1));
System.out.println(myListOfParameters.get(2));
};
myFunction.accept(Arrays.asList(
"firstAString",
true,
1));
Outputs :
firstAString
true
1
type of the param: class java.lang.String
type of the param: class java.lang.Boolean
type of the param: class java.lang.Integer
firstAString
true
1
Upvotes: 0
Reputation: 951
For 3 and more arguments you could use curried(http://en.wikipedia.org/wiki/Currying) functions with last consumer:
Function<Double, Function<Integer, Consumer<String>>> f = d -> i -> s -> {
System.out.println("" + d+ ";" + i+ ";" + s);
};
f.apply(1.0).apply(2).accept("s");
Output is:
1.0;2;s
It's enough to have a function of one argument to express function of any number of arguments: https://en.wikipedia.org/wiki/Currying#Lambda_calculi
Upvotes: 59
Reputation: 111
It's pretty easy to define Functional Interfaces for yourself. In the project I am currently working on, I have found several occasions where passing a method as an argument has allowed me to keep my code nice and DRY. Here are some functional interfaces I've defined:
@FunctionalInterface
public interface CheckedVoidFunction {
void apply() throws Exception;
}
@FunctionalInterface
public interface VoidFunction {
void apply();
}
@FunctionalInterface
public interface SetCategoryMethod {
void accept(ObservableList<IRegionWithText> list, Document document, String pattern);
}
Here are a method that accepts these kinds of arguments:
private void loadAnnotations(String annotationType, VoidFunction afterLoadFunction, List<Path> documentPaths) {
And here is where I call these methods with different methods as arguments:
loadAnnotations(DocumentReader.REDACTION_ANNOTATION_TYPE, this::afterLoadRedactions, documentPaths);
loadAnnotations(DocumentReader.HIGHLIGHT_ANNOTATION_TYPE, this::afterLoadHighlights, documentPaths);
There may be better ways to do this, but I thought I'd share what has been useful for me.
Upvotes: 11
Reputation: 7936
By default you're limited with only java.util.function.Consumer
and java.util.function.BiConsumer
. They are sufficient for current java streaming API. But you can create your own functional interfaces that will receive as many arguments as you like and use it in your own custom APIs.
Upvotes: 46
Reputation: 533870
Is there any easy way to do it, which does not require defining a new functional interface?
An object can contain any number of other objects. The Streams API is designed to stream just one object at a time, and if you need more you would wrap these in an object which holds them all.
e.g.
Map<String, Long> map = new HashMap<>();
// each entry has a key and a value as Map.Entry<String, Long>
Map<Long, String> revMap = map.entrySet().stream()
.collect(groupingBy(Entry::getValue(), HashMap::new, Entry::getKey));
Upvotes: 3