While manipulating Java 8 streams I've encountered an error where the compiler seems to 'forget' the type my generic parameters.
The following snippet creates a stream of class names and attempts to map the stream to a stream of Class<? extends CharSequence>.
public static Stream<Class<? extends CharSequence>> getClasses() {
return Arrays.asList("java.lang.String", "java.lang.StringBuilder", "Kaboom!")
.stream()
.map(x -> {
try {
Class<?> result = Class.forName(x);
return result == null ? null : result.asSubclass(CharSequence.class);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
})
//.filter(x -> x != null)
;
}
When I uncomment the filter to remove the null entries from the stream I get a compile error
Type mismatch: cannot convert from Class<capture#15-of ? extends CharSequence> to Class<Object>
Can someone please explain to me why adding the filter causes this error?
PS: The code here is somewhat arbitrary and it's easy enough to make the error go away: Assign the mapped stream to a temporary variable before applying the filter. What I'm interested in is why the above code snippet generates a compile time error.
Edit: As @Holger pointed out the this question is not an exact duplicate of Java 8 Streams: why does Collectors.toMap behave differently for generics with wildcards? because the problematic snippet there currently compiles without issues while the snippet here does not.
asSubclassreally returns aClass<? extends CharSequence>. It doesn't compile withjavacalso, at least 1.8.0_74.filterwith an explicit type argument tomap:.<Class<? extends CharSequence>>map(. Or I can get it to compile withreturn CharSequence.classinstead ofreturn nullafter thecatchblock. It looks like a problem with type inference.Stream.of(…)instead ofArrays.asList(…).stream(), furtherClass.forNamenever returnsnullso the conditional is obsolete. And you can always merge a.map(…).filter(…)usingflatMap(…)when thefilteris merely handling themap’s error condition (i.e. anulltest). Putting it all together, you can solve your task asreturn Stream.of("java.lang.String", "java.lang.StringBuilder", "Kaboom!") .flatMap(x -> { try { return Stream.of(Class.forName(x).asSubclass(CharSequence.class)); } catch(Exception e) { e.printStackTrace(); return null; }});.map(Function.identity())after the rejected.filter(…)makes the error disappear. Chaining another.filter(x->true)or even a simple.unordered()makes it reappear and adding anothermap(x->x)or.flatMap(Stream::of)will fix it again. You can go on with that…the only thing that matters is the last operation of the chain.