Type inference: bug or feature?
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Mon Jul 27 10:26:40 UTC 2020
CC'ing compiler-dev
Hi Justin,
the behavior you are observing is normal. For a Java expression to be
able to be influenced by type inference it has to be a poly expression
(where poly stands for _many_ as in, the same expression can have many
types).
At the time we did Java 8 we briefly considered making cast expressions
part of the poly expression dance (like lambdas, method references,
method calls, parens, new creation expression, conditionals and switch
expression - see JLS 15.2), but the rule which dictate cast conversion
(JLS 5.5) are so complex (as they have to take into account possibility
for unchecked cast, etc.) that it felt like touching them was a very
risky move, with no clear benefit.
The behavior you see is caused by the fact that the cast expression acts
as a "shield" - that is, whenever a method call appears after a cast
expression (as in your case), the method call is type-checked as if it
were in isolation, and _then_ the result of type checking is validated
against the cast. In other words, your example is no different than doing:
var x = (List<Integer>)(Object)emptyList(Number.class);
That is, the emptyList call will see no meaningful target type (just
Object), so Number will be inferred and a List<Number> will be returned,
which will then be incompatible with the type of the cast expression
(List<Integer>).
Your second set of examples, since it does not make use of cast
expressions, works as expected, as the target type can freely flow
inside the method call typing, and thus influence the type inference
result (e.g. the inference engine now sees two constraints, for Integer
and for Number, and is of course able to pick the "best" one).
Hope this helps.
Cheers
Maurizio
On 26/07/2020 18:22, Justin Dekeyser wrote:
> Dear all,
>
> I'm not sure but I think I've found a bug in Java type inference mechanism.
> It may also be a feature, so I'll expose the problem to you below; in terms
> of Integer, Number and List, although you'll quickly realize it will work
> wrong in any similar situation.
>
> Let's assume I have
>
> static <U, V extends U> List<U> emptyList(Class<U> magnet) {
> return Collections.emptyList();
> }
>
> Then the following codes does not compile (for the same reason):
>
> var x = (List<Integer>) emptyList(Number.class);
> List<Integer> x = (List<Integer>) emptyList(Number.class);
>
> incompatible types: List<Number> cannot be converted to List<Integer>
>
> however, the following do compile:
>
> var x = emptyList(Number.class); // inferred to List<Number>
> List<Integer> x = emptyList(Number.class); // no mistake here, it's Integer
> on the left
>
> Is this the expected behavior? Why does casting operation here interfere
> with type inference like this?
>
> Regards,
>
> Justin Dekeyser
>
>
>
>
> <https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
> Garanti
> sans virus. www.avast.com
> <https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
> <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
More information about the core-libs-dev
mailing list