Lambda and wildcard

Tagir Valeev amaembo at gmail.com
Fri Oct 10 13:26:54 UTC 2025


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:
>>    -
>>
>>       The result of the function type is void.
>>       -
>>
>>        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/25d2efdf/attachment-0001.htm>


More information about the compiler-dev mailing list