Lambda and wildcard

Vicente Romero vicente.romero at oracle.com
Fri Oct 10 15:05:03 UTC 2025


Hi Tagir,

I had already created [1]. Sorry for not sending an update yesterday,

Thanks,
Vicente

[1] https://bugs.openjdk.org/browse/JDK-8369517

On 10/10/25 09:26, Tagir Valeev wrote:
> Here it is:
>
> https://bugs.openjdk.org/browse/JDK-8369565
>
> I checked a few random OpenJDK builds I have on my machine, and it 
> looks like the behavior was like this since Java 8.
>
> With best regards,
> Tagir Valeev
>
> On Thu, Oct 9, 2025 at 1:46 PM Maurizio Cimadamore 
> <maurizio.cimadamore at oracle.com> wrote:
>
>     Yes, please!
>
>     Thanks
>     Maurizio
>
>     On 09/10/2025 10:18, Tagir Valeev wrote:
>
>>     Hello, Maurizio!
>>
>>     Thank you for clarifying this. Should I file an issue?
>>
>>     With best regards,
>>     Tagir Valeev
>>
>>     On Thu, Oct 9, 2025 at 11:00 AM Maurizio Cimadamore
>>     <maurizio.cimadamore at oracle.com> wrote:
>>
>>         Hi Tagir!
>>
>>         Nice cacth.
>>
>>         Here's a slightly different example which reveals a bit more
>>         of what's happening:
>>
>>         ```
>>         interface Main {
>>             interface X<T> {
>>                 X<T> self();
>>             }
>>
>>             static X<?> makeX() {return null;}
>>
>>             static <R> X<R> create(Supplier<? extends R> supplier)
>>         {return null;}
>>
>>             static X<X<?>> methodRef() {
>>                 var s = (String)create(Main::makeX);
>>             }
>>
>>             static X<X<?>> lambda() {
>>                 var s = (String)create(() -> makeX());
>>             }
>>         }
>>         ```
>>
>>         This prints:
>>
>>         ```
>>         error: incompatible types: X<X<?>> cannot be converted to String
>>                 var s = (String)create(Main::makeX);
>>                                       ^
>>         error: incompatible types: X<X<CAP#1>> cannot be converted to
>>         String
>>                 var s = (String)create(() -> makeX());
>>                                       ^
>>           where CAP#1 is a fresh type-variable:
>>             CAP#1 extends Object from capture of ?
>>         ```
>>
>>         So, the main difference between the two examples is that in
>>         the lambda case, the return type of the makeX call is
>>         captured. But in the method refreene case no capture occurs.
>>
>>         I believe the lambda case works as expected, but the method
>>         reference case does not.
>>
>>         The JLS mandates a capture of the resolved method return type
>>         (JLS 15.13.2, emphasis mine):
>>
>>>         A method reference expression is /congruent/ with a function
>>>         type if both of the following are true:
>>>
>>>          *
>>>
>>>             The function type identifies a single compile-time
>>>             declaration corresponding to the reference.
>>>
>>>          *
>>>
>>>              One of the following is true:
>>>
>>>              o
>>>
>>>                 The result of the function type is |void|.
>>>
>>>              o
>>>
>>>                  The result of the function type is R, *and the
>>>                 result of applying capture conversion (§5.1.10
>>>                 <https://docs.oracle.com/javase/specs/jls/se25/html/jls-5.html#jls-5.1.10>)
>>>                 to the return type of the invocation type
>>>                 (§15.12.2.6
>>>                 <https://docs.oracle.com/javase/specs/jls/se25/html/jls-15.html#jls-15.12.2.6>)
>>>                 of the chosen compile-time declaration is R'* (where
>>>                 R is the target type that may be used to infer R'),
>>>                 and neither R nor R' is |void|, and R' is compatible
>>>                 with R in an assignment context.
>>>
>>         It seems like javac is missing this capture conversion and,
>>         because of that, the behavior of the two examples diverge.
>>
>>         Cheers
>>         Maurizio
>>
>>
>>
>>
>>
>>         On 07/10/2025 17:12, Tagir Valeev wrote:
>>>         Hello!
>>>
>>>         I'm investigating a seemingly weird compilation case.
>>>         Consider the following Java interface:
>>>
>>>         import java.util.function.Supplier;
>>>
>>>         interface Main {
>>>             interface X<T> {
>>>                 X<T> self();
>>>             }
>>>
>>>             static X<?> makeX() {return null;}
>>>
>>>             static <R> X<R> create(Supplier<? extends R> supplier)
>>>         {return null;}
>>>
>>>             static X<X<?>> methodRef() {
>>>                 return create(Main::makeX).self();
>>>             }
>>>
>>>             static X<X<?>> lambda() {
>>>                 return create(() -> makeX()).self();
>>>             }
>>>         }
>>>
>>>         I expect that either both methods 'methodRef' and 'lambda'
>>>         should be compilable or both should be non-compilable.
>>>         However, while 'methodRef' compiles, 'lambda' is rejected by
>>>         compiler (using javac build 25+36-3489):
>>>
>>>         Main.java:17: error: incompatible types: X<X<CAP#1>> cannot
>>>         be converted to X<X<?>>
>>>                 return create(() -> makeX()).self();
>>>                                                  ^
>>>           where CAP#1 is a fresh type-variable:
>>>             CAP#1 extends Object from capture of ?
>>>         1 error
>>>         error: compilation failed
>>>
>>>         Could you please help me and clarify whether this is an
>>>         expected behavior or not?
>>>
>>>         With best regards,
>>>         Tagir Valeev
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20251010/e5db56d2/attachment-0001.htm>


More information about the compiler-dev mailing list