Unexpected error compiling generic stream() code in JDK 17 and JDK 19 (but compiles in JDK 8)

Jon Mann jon at spinis-associates.co.uk
Mon Jan 23 11:02:22 UTC 2023


Hello,

While updating a large code base
I encountered a compilation error
in JDK 17 and JDK 19
which does not happen in JDK 8.

I eventually managed to reproduce it
in the sample code below.

JDK 8 compiles the code OK.

JDK 17 and JDK 19 both produce the following error:

     error: getContext() in AbstractSomething cannot implement 
getContext() in Something
       return type CAP#1 is not compatible with CAP#2
       where Ctx#1,Ctx#2 are type-variables:
         Ctx#1 extends Context declared in class AbstractSomething
         Ctx#2 extends Context declared in interface Something
       where CAP#1,CAP#2 are fresh type-variables:
         CAP#1 extends Context from capture of ? extends Context
         CAP#2 extends Context from capture of ? extends Context
     1 error

The error does not include any source filenames or line numbers.

The error suggests that something is wrong in the AbstractSomething class
but the actual cause of the problem
seems to be the generics of the stream() code in the getSomethings() method
which is not mentioned in the error.

JDK 17 and JDK 19 will compile the code
after replacing .map(c -> construct(c))
with .map(this::construct)

JDK 8 compiles either version of the code.

This feels like a regression in the compiler
but maybe I am missing something?

Thanks,
Jon.



===========
SAMPLE CODE
===========

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test {

     List<Something> getSomethings() {
         return Stream.of(
                         SomethingA.class,
                         SomethingB.class)
                 .map(c -> construct(c)) // UNEXPECTED COMPILER ERROR IN 
JDK 17+19 (BUT COMPILES IN JDK 8)
                 // .map(this::construct) // COMPILES IN JDK 17+19+8
                 .collect(Collectors.toList());
     }

     Something construct(Class<? extends Something> clazz) {
         return null;
     }

     interface Context { }
     interface ContextA extends Context { }
     interface ContextB extends Context { }

     interface Something<Ctx extends Context> {
         Ctx getContext();
     }

     interface SpecificSomething<Ctx extends Context>
             extends Something<Ctx> { }

     abstract static class AbstractSomething<Ctx extends Context>
             implements Something<Ctx> {

         @Override
         public Ctx getContext() {
             return null;
         }
     }

     static class SomethingA
             extends AbstractSomething<ContextA>
             implements SpecificSomething<ContextA> { }

     static class SomethingB
             extends AbstractSomething<ContextB>
             implements SpecificSomething<ContextB> { }
}





More information about the compiler-dev mailing list