JDK-8326486 : Something wrong with type inference.
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 11,17,21
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2024-02-22
  • Updated: 2024-02-23
  • Resolved: 2024-02-23
Related Reports
Duplicate :  
Description
A DESCRIPTION OF THE PROBLEM :
Under certain conditions, compiler somehow fails to correctly infer type of stream, and defaults to Object.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the enclosed test case and see the compiler to fail.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code compiles.
ACTUAL -
The compiler fails with error:

JavaBug.java:36: error: cannot find symbol
                flattened.map(b -> b.intValue());
                                    ^
  symbol:   method intValue()
  location: variable b of type Object

suggesting that it failed to infer the type of items in the streams to be of type Number.

---------- BEGIN SOURCE ----------
import java.util.function.Function;
import java.util.stream.Stream;

public class JavaBug {

	/**
	 * This creates stream of streams of mixed types with some base, and flattens it with flatMap(identity).
	 * 
	 * As long as the stream is stored to variable explicitly typed, everything is good. 
	 */
	public void good1() {
		Stream<? extends Number> flattened = Stream.of(Stream.of(1), Stream.of(1L)).flatMap(Function.identity());
			
		flattened.map(b -> b.intValue());
	}

	/**
	 * This one uses implicit type of mixed stream, but does not use Function.identity(), it uses explicit lambda instead. 
	 * 
	 * This is good too.
	 */
	public void good2() {
		var flattened = Stream.of(Stream.of(1), Stream.of(1L)).flatMap(x -> x);
			
		flattened.map(b -> b.intValue());
	}

	/**
	 * If implicit type of mixed stream is used, and Function.identity is used to flatten the streams, compiler fails.
	 */
	public void failure() {
		var flattened = Stream.of(Stream.of(1), Stream.of(1L)).flatMap(Function.identity());
			
		flattened.map(b -> b.intValue());
	}
	
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
You must either split the pipeline to explicitly name the type of the inferred stream.

Or do not use Function.identity, use explicit lambda instead.

FREQUENCY : always



Comments
This is another incarnation of JDK-8206142. Simpler reproducer: Stream<Stream<? extends Number>> ss = null; String s = (String)ss.flatMap(Function.identity()); The error message here gives the inferred type away: error: incompatible types: Stream<Object> cannot be converted to String String s = (String)ss.flatMap(Function.identity()); ^ If the wildcard is removed from "ss" (e.g. the type is just Stream<Stream<Number>>) the inferred type is Stream<Number> (as expected). The problem is that we end up in this situation for Function.identity's T type parameter (where R is flatMap's parameter): Stream<? extends Number> <: T <: Stream<? extends R> So, javac will attempt to do incorporation, doing this subtyping check: Stream<? extends Number> <: Stream<? extends R> But this will apply capture conversion on the LHS, and result inferring R = #CAP, where #CAP <: Number. Unfortunately, this resolution will fail the bound check when sanity-tested again (as a new capture will be generated then), so javac bails out and Object is inferred instead. (Note that JDK-8206142 cannot be fixed w/o a spec clarification on how to deal with capture conversion inside incorporation checks, which is likely to require a lot of work).
23-02-2024