Java 8 compiler bug?
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Thu Nov 17 12:44:00 UTC 2016
On 15/11/16 23:46, Oliver Gierke wrote:
> CriteriaBuilder and all other types come from JPA. The method in question has two overloads:
>
> <E, C extends Collection<E>> Predicate isNotMember(Expression<E> elem, Expression<C> collection);
> <E, C extends Collection<E>> Predicate isNotMember(E elem, Expression<C> collection);
>
> I fail to understand why the second method is even considered as that create a contradiction between the bound values of E (Expression<…>) and C (Collection<Object>).
Hi Oliver,
I believe the JDK 8 behavior is correct here - let's just consider your
second method - which is the one you have doubts about:
<E, C extends Collection<E>> Predicate isNotMember(E elem, Expression<C> collection);
Now, you want to pass the following actual argument types to this method:
arg1 = Expression<Object>
arg2 = Expression<Collection<Object>>
So, let's look at which constraints these argument cause trigger on our
inference variables E and C.
So, since Expression<Object> must be compatible with E - we have that
Expression<Object> <: E
Then, since also Expression<Collection<Object>> must be compatible with
Expression<C> we have that:
Expression<Collection<Object>> <: C
We also have another source of constraint - namely the declared bound:
C <: Collection<E>
Now, from these constraints, (and this is the step that is new in Java 8
and newer), we can do _incorporation_ meaning we can derive more
constraints by looking at useful patterns. There's one notable pattern here:
Expression<Collection<Object>> <: C
C <: Collection<E>
By transitivity, this should imply that:
Expression<Collection<Object>> <: Collection<E>
which would then derive an additional constraint:
E = Object.
So, what you end up inferring is E = Object and C = Collection<Object>,
and this inference is possible thanks to the transitivity treatment that
is guaranteed by the incorporation step that is new in the Java 8
specifications (see section 18.3).
Java 7 did not do anything special to enforce transitivity (which often
result in far less precise inference results and spurious errors) - that
meant that the only constraints the compiler had in 7 were:
Expression<Object> <: E
C <: Collection<E>
From which you infer E = Expression<Object> and C = Collection<Object>
which is an answer incompatible with declared bounds - so Java 7 was
able to discard your second method as not applicable. But it was doing
so only because the Java 7 inference machinery was not able to find an
answer to the inference problem.
Maurizio
More information about the compiler-dev
mailing list