ewok
ewok

Reputation: 21443

Passing named functions as arguments

Java 8 added lambda expressions. Using lambdas in a similar fashion to anonymous classes is pretty straight forward, but I'm wondering if the related functionality of using named functions as arguments to other functions exists. For example, is there a Java way to write the following Python code:

list1 = (1,2,3)
list2 = (4,5,6)

def add(a, b):
  return a+b

for e in map(add, list1, list2):
  print(e)

output

5
7
9

Upvotes: 5

Views: 1716

Answers (4)

Stuart Marks
Stuart Marks

Reputation: 132350

It's certainly possible to declare variables whose types are functional interfaces and then assign lambdas to them. That's the conclusion of this answer and its comment thread. For example, one might have a declaration like this:

BiFunction<Integer,Integer,Integer> add = (a, b) -> a + b;

This isn't exactly what the OP asked for. The Python equivalent of this would be something like:

add = lambda a, b: a + b

This isn't a named function; it's a variable whose value is a (lambda) function. The original question was asking about using named function in Java. The way to do this is with a method reference.

Suppose you have method that takes a BiFunction<Integer,Integer,Integer> parameter. You could pass it the add variable as defined above. But you could also declare an ordinary method, and use it as the target of a method reference. The trick is to make sure that the "shape" (number and type of arguments, type of return value) matches whatever functional interface you're trying to use. In this case you have two parameters of type Integer and the return value is Integer, so your method would be

static Integer add(Integer a, Integer b) {
    return a + b;
}

Then you can refer to this using a method reference of the form MyClass::add.

Note that I've made this a static method. If it were an instance method, the receiver (this) is a hidden parameter that you have to take into account when matching it to a functional interface.

Note also that because of auto-boxing, one could also write:

static int add(int a, int b) {
    return a + b;
}

There's certainly a use for assigning lambdas to variables, such as if the choice of an algorithm is made at runtime. But if the lambdas are all being assigned to constant fields, it's often easier to use method references that target ordinary methods.

Upvotes: 0

mkobit
mkobit

Reputation: 47249

Well, there are no "functions" in Java, only methods, so you would not get the same exact thing.

The best way to achieve this is to use method references, and in this case a BiConsumer could be used:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;

public class StackOverflow {

    public static void main(String[] args) throws InterruptedException {
        List<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);

        List<Integer> list2 = new ArrayList<>();
        list2.add(4);
        list2.add(5);
        list2.add(6);

        mapOver(StackOverflow::add, list1, list2);
    }

    public static void add(Integer a, Integer b) {
        System.out.println(a + b);
    }

    public static <T> void mapOver(BiConsumer<T, T> function,
                               Iterable<T> first,
                               Iterable<T> second) {
        final Iterator<T> firstIterator = first.iterator();
        final Iterator<T> secondIterator = second.iterator();
        while (firstIterator.hasNext() && secondIterator.hasNext()) {
            function.accept(firstIterator.next(), secondIterator.next());
        }
    }
}

Output:

5
7
9

Upvotes: 2

John Kugelman
John Kugelman

Reputation: 361565

Yes, you can use method references like Integer::sum where lambdas are allowed.

int six = IntStream.of(1, 2, 3)
    .reduce(0, Integer::sum);

This is equivalent to

int six = IntStream.of(1, 2, 3)
    .reduce(0, (a, b) -> Integer.sum(a, b));

Methods like Integer.sum and Double.max were added in Java 8 precisely so they could be used in lambdas like this.

There's no built-in way to "zip" together multiple lists the way Python does, though.

Upvotes: 4

miki
miki

Reputation: 695

Not sure what your question really is, but in Java, this would be something like..

List<Integer list1 = Arrays.asList(1,2,3);
List<Integer list2 = Arrays.asList(4,5,6);

IntStream.range(0, list1.size())
    .parallel()
    .forEach(i -> {
        System.out.println(list1.get(i) + list2.get(i));
    });

.. since you can't stream multiple collections in Java as you can in python.

Upvotes: 0

Related Questions