Explicit captures in lambda expression

Quân Anh Mai anhmdq at gmail.com
Tue Jan 7 05:46:25 UTC 2025


To add more context regarding JDK-8347047, even without the core lib bug,
the method is easily misused. The method in consideration looks like this:

MemorySegment MemorySegment::reinterpret(long size, Consumer<MemorySegment>
cleanup)

`cleanup` is triggered when the returned segment is closed, which for some
kind of MemorySegment, is at the time the old MemorySegment instance (this)
and the new MemorySegment instance (the returned segment) are
garbage-collected. As a result, `cleanup` must not reference the old
segment instance. It is given a freshly created MemorySegment instance and
must use it. However, it is easy to mistakenly use the old instance instead:

MemorySegment oldMs;
oldMs.reinterpret(size, ms -> free(oldMs));

While it should be:

oldMs.reinterpret(size, ms -> free(ms));

With explicit captures, it is easy to see if oldMs is mistakenly captured
as this would not compiled:

oldMs.reinterpret(size, [](ms) -> free(oldMs));

Cheers,
Quan Anh

On Tue, 7 Jan 2025 at 12:30, Quân Anh Mai <anhmdq at gmail.com> wrote:

> Hi,
>
> A feature I missed from C++ is explicit captures in lambda expressions and
> from recent events I think that it may be a valuable feature to have in
> Java.
>
> JEP-8341785[1] proposes treating loop variables as final inside the loop
> body, the main motivation is to allow their usage in lambda expressions
> inside the loop body. However, it seems to be withdrawn due to the concern
> of making an exception. I believe explicit captures would remove the
> condition that the variable being captured needs to be effectively final,
> resolving the motivation of JEP-8341785 and many other cases.
>
> JDK-8347047[2] is a bug caused by the implicit capture of the this
> reference inside a lambda, which leads to the object never being reclaimed
> by the garbage collector, the thing we actually want to capture there is
> the result of the method call this.address(). In this case, implicit
> captures is very error-prone and it would be better if we can explicitly
> declare what we want to capture instead to avoid accidentally capturing
> unwanted variables.
>
> As a result, I propose that:
>
> - A lambda expression can optionally have a captures, if that is the case,
> all the variables being captured by the lambda expression need to be
> declared inside it.
> - We can capture an effectively final variable without giving it another
> name, but for the others, to avoid confusion, another name for the variable
> is needed.
>
> E.g, suppose a, b are effectively final variables and x, y are not:
>
> () -> a; // Allowed, implicit capture of a
> []() -> a; // Disallowed, a is not captured and hence not defined here
> [a]() -> a; // Allowed, explicit capture of a
> [a, b]() -> a + b; // Allowed, capture 2 variables
> () -> x; // Disallowed, cannot implicitly capture non-effectively-final
> variable
> [x]() -> x; // Disallowed, capture a non-effectively-final without
> reassignment
> [t = x]() -> x; // Disallowed, x is not defined here
> [t = x]() -> t; // Allowed, x is captured and assigned to the variable t
> inside the lambda
> [t = this.address()]() -> t; // Maybe?
>
> Cheers,
> Quan Anh
>
> [1]: https://openjdk.org/jeps/8341785
> [2]: https://bugs.openjdk.org/projects/JDK/issues/JDK-8347047
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20250107/7c8d3969/attachment-0001.htm>


More information about the amber-dev mailing list