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