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.