return only returns from the lambda and Execute-around Pattern


`return` only returns from the lambda, not the outer method

The return method only returns from the lambda, not the outer method.

Beware that this is different from Scala and Kotlin!

void threeTimes(IntConsumer r) {
	for (int i = 0; i < 3; i++) {
		r.accept(i);
	}
}
 
void demo() {
	threeTimes(i -> {
		System.out.println(i);
		return; // Return from lambda to threeTimes only!
	});
}

This can lead to unexpected behavior when attempting to write own language constructs, as in builtin constructs such as for loops return behaves differently:

void demo2() {
	for (int i = 0; i < 3; i++) {
		System.out.println(i);
		return; // Return from 'demo2' entirely
	}
}

In Scala and Kotlin, demo and demo2 would both only print 0. But this is not more consistent. The Java approach is consistent with refactoring and the use of classes - the return in the code at the top, and the code below behaves the same:

void demo3() {
	threeTimes(new MyIntConsumer());
}
 
class MyIntConsumer implements IntConsumer {
	public void accept(int i) {
		System.out.println(i);
		return;
	}
}

Therefore, the Java return is more consistent with class methods and refactoring, but less with the for and while builtins, these remain special.

Because of this, the following two are equivalent in Java:

IntStream.range(1, 4)
	.map(x -> x * x)
	.forEach(System.out::println);
IntStream.range(1, 4)
	.map(x -> { return x * x; })
	.forEach(System.out::println);

Furthermore, the use of try-with-resources is safe in Java:

class Resource implements AutoCloseable {
	public void close() {
		System.out.println("close()");
	}
}
 
void executeAround(Consumer<Resource> f) {
	try (Resource r = new Resource()) {
		System.out.print("before ");
		f.accept(r);
		System.out.print("after ");
	}
}
 
void demo4() {
	executeAround(r -> {
		System.out.print("accept() ");
		return; // Does not return from demo4, but frees the resource.
	});
}

will print before accept() after close(). In the Scala and Kotlin semantics, the try-with-resources would not be closed, but it would print before accept() only.


Lambdas and Execute-around Pattern

There are several good examples of using lambdas as a FunctionalInterface in simple scenarios. A fairly common use case that can be improved by lambdas is what is called the Execute-Around pattern. In this pattern, you have a set of standard setup/teardown code that is needed for multiple scenarios surrounding use case specific code. A few common example of this are file io, database io, try/catch blocks.

interface DataProcessor {
	void process( Connection connection ) throws SQLException;;
}
 
public void doProcessing( DataProcessor processor ) throws SQLException{
	try (Connection connection = DBUtil.getDatabaseConnection();) {
		processor.process(connection);
		connection.commit();
	}
}

Then to call this method with a lambda it might look like:

public static void updateMyDAO(MyVO vo) throws DatabaseException {
     doProcessing((Connection conn) -> MyDAO.update(conn, ObjectMapper.map(vo)));
}

This is not limited to I/O operations. It can apply to any scenario where similar setup/tear down tasks are applicable with minor variations. The main benefit of this Pattern is code re-use and enforcing DRY (Don't Repeat Yourself).


Using lambda expressions & predicates to get a certain value(s) from a list

Starting with Java 8, you can use lambda expressions & predicates.

Example: Use a lambda expressions & a predicate to get a certain value from a list. In this example every person will be printed out with the fact if they are 18 and older or not.

Person Class:

public class Person {
	private String name;
	private int age;
 
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
 
	public int getAge() {
		return age;
	}
 
	public String getName() {
		return name;
	}
}

The built-in interface Predicate from the java.util.function.Predicate packages is a functional interface with a boolean test(T t) method.

Example Usage:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
 
public class LambdaExample {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Jeroen", 20));
		personList.add(new Person("Jack", 5));
		personList.add(new Person("Lisa", 19));
 
		print(personList, p -> p.getAge() >= 18);
	}
 
	private static void print(List<Person> personList, Predicate<Person> checker) {
		for (Person person : personList) {
			if (checker.test(person)) {
				System.out.print(person + " matches your expression.");
			} else {
				System.out.println(person + " doesn't match your expression.");
			}
		}
	}
}

The print(personList, p -> p.getAge() >= 18); method takes a lambda expression (because the Predicate is used a parameter) where you can define the expression that is needed. The checker's test method checks if this expression is correct or not: checker.test(person).

You can easily change this to something else, for example to print(personList, p -> p.getName().startsWith("J"));. This will check if the person's name starts with a "J"

Basic Programs