218

In Java, is it possible to have a lambda accept multiple different types?

I.e: Single variable works:

    Function <Integer, Integer> adder = i -> i + 1;
    System.out.println (adder.apply (10));

Varargs also work:

    Function <Integer [], Integer> multiAdder = ints -> {
        int sum = 0;
        for (Integer i : ints) {
            sum += i;
        }
        return sum;
    };

    //.... 
    System.out.println ((multiAdder.apply (new Integer [] { 1, 2, 3, 4 })));

But I want something that can accept many different types of arguments, e.g:

    Function <String, Integer, Double, Person, String> myLambda = a , b, c, d->  {
    [DO STUFF]
    return "done stuff"
    };

The main use is to have small inline functions inside functions for convenience.

I've looked around google and inspected Java's Function Package, but could not find. Is this possible?

0

9 Answers 9

232

It's possible if you define such a functional interface with multiple type parameters. There is no such built in type. (There are a few limited types with multiple parameters.)

@FunctionalInterface
interface Function6<One, Two, Three, Four, Five, Six> {
    public Six apply(One one, Two two, Three three, Four four, Five five);
}

public static void main(String[] args) throws Exception {
    Function6<String, Integer, Double, Void, List<Float>, Character> func = (a, b, c, d, e) -> 'z';
}

I've called it Function6 here. The name is at your discretion, just try not to clash with existing names in the Java libraries.


There's also no way to define a variable number of type parameters, if that's what you were asking about.


Some languages, like Scala, define a number of built in such types, with 1, 2, 3, 4, 5, 6, etc. type parameters.

Sign up to request clarification or add additional context in comments.

2 Comments

You can always use Currying: Function<One, Function<Two, Function<Three, Function<Four, Function<Five, Six>>>>> func = a -> b -> c -> d -> e -> 'z';
Currying? Wow that is much worse than a functional interface.
71

For something with 2 parameters, you could use BiFunction. If you need more, you can define your own function interface, like so:

@FunctionalInterface
public interface FourParameterFunction<T, U, V, W, R> {
    public R apply(T t, U u, V v, W w);
}

If there is more than one parameter, you need to put parentheses around the argument list, like so:

FourParameterFunction<String, Integer, Double, Person, String> myLambda = (a, b, c, d) -> {
    // do something
    return "done something";
};

Comments

56

For this case you could use interfaces from default library (java 1.8):

java.util.function.BiConsumer
java.util.function.BiFunction

There is a small (not the best) example of default method in interface:

default BiFunction<File, String, String> getFolderFileReader() {
    return (directory, fileName) -> {
        try {
            return FileUtils.readFile(directory, fileName);
        } catch (IOException e) {
            LOG.error("Unable to read file {} in {}.", fileName, directory.getAbsolutePath(), e);
        }
        return "";
    };
}}

3 Comments

You will get more up-votes from Java8 fans if you amend your question to illustrate how those interfaces can be used to satisfy the requirement.
BiFunction allows you to define only two-argument functions, the question is about functions with any number of arguments
@DmitryKlochkov No it isn't. It is about a function with four arguments and one result type. Nothing anywhere about any number of arguments.
23

To make the use of lambda : There are three type of operation:
1. Accept parameter --> Consumer
2. Test parameter return boolean --> Predicate
3. Manipulate parameter and return value --> Function

Java Functional interface upto two parameter:
Single parameter interface
Consumer
Predicate
Function

Two parameter interface
BiConsumer
BiPredicate
BiFunction

For more than two, you have to create functional interface as follow(Consumer type):

@FunctionalInterface
public interface FiveParameterConsumer<T, U, V, W, X> {
    public void accept(T t, U u, V v, W w, X x);
}

2 Comments

There are four types of operation. You left out Supplier.
Supplier doesn't seem to be useful here. Increasing one more parameter has no use, since you can return only one parameter, the method must remain void.
4

You could also use jOOL library - https://github.com/jOOQ/jOOL

It has already prepared function interfaces with different number of parameters. For instance, you could use org.jooq.lambda.function.Function3, etc from Function0 up to Function16.

1 Comment

Another popular library that offers similar predefined interfaces would be Vavr.
2

Some lambda function :

import org.junit.Test;
import java.awt.event.ActionListener;
import java.util.function.Function;

public class TestLambda {

@Test
public void testLambda() {

    System.out.println("test some lambda function");

    ////////////////////////////////////////////
    //1-any input | any output:
    //lambda define:
    Runnable lambda1 = () -> System.out.println("no parameter");
    //lambda execute:
    lambda1.run();


    ////////////////////////////////////////////
    //2-one input(as ActionEvent) | any output:
    //lambda define:
    ActionListener lambda2 = (p) -> System.out.println("One parameter as action");
    //lambda execute:
    lambda2.actionPerformed(null);


    ////////////////////////////////////////////
    //3-one input | by output(as Integer):
    //lambda define:
    Function<String, Integer> lambda3 = (p1) -> {
        System.out.println("one parameters: " + p1);
        return 10;
    };
    //lambda execute:
    lambda3.apply("test");


    ////////////////////////////////////////////
    //4-two input | any output
    //lambda define:
    TwoParameterFunctionWithoutReturn<String, Integer> lambda4 = (p1, p2) -> {
        System.out.println("two parameters: " + p1 + ", " + p2);
    };
    //lambda execute:
    lambda4.apply("param1", 10);


    ////////////////////////////////////////////
    //5-two input | by output(as Integer)
    //lambda define:
    TwoParameterFunctionByReturn<Integer, Integer> lambda5 = (p1, p2) -> {
        System.out.println("two parameters: " + p1 + ", " + p2);
        return p1 + p2;
    };
    //lambda execute:
    lambda5.apply(10, 20);


    ////////////////////////////////////////////
    //6-three input(Integer,Integer,String) | by output(as Integer)
    //lambda define:
    ThreeParameterFunctionByReturn<Integer, Integer, Integer> lambda6 = (p1, p2, p3) -> {
        System.out.println("three parameters: " + p1 + ", " + p2 + ", " + p3);
        return p1 + p2 + p3;
    };
    //lambda execute:
    lambda6.apply(10, 20, 30);

}


@FunctionalInterface
public interface TwoParameterFunctionWithoutReturn<T, U> {
    public void apply(T t, U u);
}

@FunctionalInterface
public interface TwoParameterFunctionByReturn<T, U> {
    public T apply(T t, U u);
}

@FunctionalInterface
public interface ThreeParameterFunctionByReturn<M, N, O> {
    public Integer apply(M m, N n, O o);
}
}

Comments

2

You could do it like this, although this will trigger some Type Safety warning of varags usage.

public interface FunctionWithMultipleTypeParams<T, R> {
    public R apply(@SuppressWarnings("unchecked") T ...param);
}

You can define a method that use it like this:

public <T extends Object, R> R get(FunctionWithMultipleTypeParams<T,R> function, @SuppressWarnings("unchecked") T ...param) {
    return function.apply(param);
}

which can be called like this:

var result = get(p -> myOtherFunction(p[0], p[1], p[3]), p1, p2, p3);

which you won't normally use in normal cases unless you are defining some generic method wrapper.

Comments

1

Another alternative, not sure if this applies to your particular problem but to some it may be applicable is to use UnaryOperator in java.util.function library. where it returns same type you specify, so you put all your variables in one class and is it as a parameter:

public class FunctionsLibraryUse {

    public static void main(String[] args){
        UnaryOperator<People> personsBirthday = (p) ->{
            System.out.println("it's " + p.getName() + " birthday!");
            p.setAge(p.getAge() + 1);
            return p;
        };
        People mel = new People();
        mel.setName("mel");
        mel.setAge(27);
        mel = personsBirthday.apply(mel);
        System.out.println("he is now : " + mel.getAge());

    }
}
class People{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

So the class you have, in this case Person, can have numerous instance variables and won't have to change the parameter of your lambda expression.

For those interested, I've written notes on how to use java.util.function library: http://sysdotoutdotprint.com/index.php/2017/04/28/java-util-function-library/

Comments

0

I think we can pass a map as an argument and pass the different values as elements of the map.

Function <Map <String, Object>, Integer> multiAdder = i -> {
    String  param1 = (String)i.get("PARAM1");
    Integer param2 = (Integer)i.get("PARAM2");
    Double  param3 = (Double)i.get("PARAM3");

    Integer x = callAnotherMethod(param1, param2, param3);
    return x;
};

//.... 
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("PARAM1", "String Val");
paramsMap.put("PARAM2", new Integer(12));
paramsMap.put("PARAM3", new Double(45);
System.out.println ((multiAdder.apply (paramsMap )));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.