Consumer with more than one argument in Java 8?
Asked Answered
J

5

62

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?

Jewelry answered 16/7, 2014 at 11:8 Comment(4)
There's the BiConsumer<T, U> and variations of it involving primitive types (like ObjIntConsumer<T>), but no moreFromenty
As a C# developer who started w/Java 15 years ago and am currently working w/Java, I'll go on record saying that Java's Consumer/Producer/Function/etc. classes along with all of the streaming feature are severely lacking when compared to the C# counterparts. I really wish these features were more complete and seamless.Chavarria
A. Action and Func (up to 16) are built into dotnet(core). So this Single/BiSomething...geeze louise. B. For future readers, I found a "TriConsumer" in this library. You can either grab one of these, or define your own. docs.jboss.org/infinispan/9.4/apidocs/org/infinispan/util/… search.maven.org/search?q=a:infinispan-coreAuxesis
Here's a different one : mvndoc.com/c/com.evolvedbinary.j8fu/j8fu/com/evolvedbinary/j8fu/…Auxesis
O
58

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

Overstretch answered 3/9, 2014 at 15:23 Comment(5)
And, as it's already mentioned, there is no other approach w/o defining a new functional interface w/ 3 or more parameters in java8.Overstretch
Clever, but I urge anyone considering this to just create a new functional interface.Jennajenne
@Jennajenne - for one off cases, sure, but if you need to write generic code (e.g., for a framework) that handles these multi-consumer cases, you'll quickly run into combinatorial explosion trying to cover all the bases...Isidora
Is it possible to use this with java.util.stream.Stream.forEach(Consumer<? super T> action) ?Septarium
@Septarium why not: IntStream.range(0,10).forEach(elem -> f.apply(1.0).apply(elem).accept("s"));Overstretch
L
43

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.

Lockett answered 16/7, 2014 at 11:21 Comment(2)
But, is it a good idea to have MultiConsumer ? Could it be possible that our code has design problems if we need to make a MultiConsumer ?Cassiodorus
@MasterJoe2 you can think of any method that returns void as a consumer method. There's no design flaw in how many arguments it takes. It could be 0 or it could be 10. If you need that method then you can create a consumer functional interface for it.Lockett
C
11

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.

Caltrop answered 24/10, 2017 at 18:55 Comment(1)
For the record: VoidFunction -> builtin Runnable.Deck
F
3

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));
Feverous answered 16/7, 2014 at 11:39 Comment(0)
D
0

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
Denotative answered 27/1 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.