Java types that you can use in a program, like int, Byte, Comparable, or String, are called denotable types. The types used by a compiler internally, like the subclass of an anonymous class, which you can't write in your program, are called non-denotable types.
Up until now, type inference with variables seemed quite easy to implement—just get the information about the values passed to a method and returned from a method, and infer the type. However, it isn't as simple as that when it comes to inference with non-denotable types—null types, intersection types, anonymous class types, and capture types.
For example, consider the following code and think about the type of the inferred variables:
// inferred type java.util.ImmutableCollections$ListN var a = List.of(1, "2", new StringBuilder());
var b = List.of(new ArrayList<String>(), LocalTime.now());
Type of variable a and b isn't a type that you would have read before. But that doesn't stop them from being inferred. The compiler infers them to a non-denotable type.