arrays and raw types in 18.2.3

Dan Smith daniel.smith at
Tue Oct 8 15:23:08 PDT 2013

Thanks for the questions.  I'm happy to help; keep them coming as you encounter points of confusion.

On Oct 8, 2013, at 2:06 PM, Stephan Herrmann <stephan.herrmann at> wrote:

> Maybe I'm slow today but the following phrase doesn't speak to me:
> 18.2.3
> o If T is an array type, T'[], then let S'[] be the most specific array type that is a supertype of S (or S itself)
> What reasons exist why S'[] would be different from S?
> If S is an array type, then S'[] = S.
> If S is not an array type,
> how can S have an array type as a supertype?

This is to account for type variables and intersection types.  I know there are some restrictions on what bounded type variables can look like in source (including their intersection upper bounds), but through things like capture or lub I'm pretty sure you can end up with cases in which both of these kinds of types can be subtypes of array types.

> Secondly, how should this constraint be reduced:
>  C <: C<α>
> According to 18.2.3 I need a parameterization of C
> that is a supertype of C (raw).
> Since no such supertype exists,
> some invocations of generic methods with raw arguments
> seem to be illegal now, which were legal in Java 7.

Before you get to subtyping, you'll usually have a compatibility constraint of the form "C -> C<α>"; see 18.2.2.

The note #1 points out that the text still needs to account for unchecked exceptions.  Actual spec text to come in the next round.  These compatibility constraints should work as before: if the statement can be made true via unchecked conversion, then the result is "true".  (And there will be an unchecked warning based on the requirement somewhere in 15.12.2.)

> I'm currently looking at this test:
> public class X {
>  public static void main(String[] args) {
>    EntityKey entityKey = null;
>    new EntityCondenser().condense(entityKey);
>  }
>  public static class EntityCondenser {
>    <I, E extends EntityType<I, E, K>, K extends EntityKey<I>> void condense(K entityKey) {
>    }
>  }
>  public class EntityKey<I> {}
>  public interface EntityType<
>    I,
>    E extends EntityType<I, E, K>,
>    K extends EntityKey<I>> {
>  }
> }

Ah, okay, in this case, there is no compatibility constraint for the two types ("Raw -> Parameterized"), just a subtyping constraint ("Raw <: Parameterized"), which is derived from the bound of K.

The old spec was somewhat vague here, but I believe the correct behavior for both 7 and 8 is to fail.  If the method is applicable, that means there exist choices for I and K such that i) K <: EntityKey<I> (*see note) and ii) (raw) EntityKey is method-invocation-compatible with K.  Under 7, what are the choices for I and K that satisfy these two assertions?  If the compiler hasn't produced them, then it hasn't proved, per or, that the method is applicable.

(*Subtyping is used here, not a more general notion of compatibility, because that's what 'extends' means when used as a variable bound.)

You could explicitly satisfy these requirements with something like

ec.<Object, ..., EntityKey<Object>>(entityKey) // unchecked conversion on entityKey

But there's no provision in the 7 or 8 specs of inference that would make it smart enough to come up with this.  Instead, the constraint "EntityKey -> α3" is always reduced to "EntityKey <: α3".  (The fact that reduction throws away the possibility that α3 = EntityKey<Whatever> here is what the Lambda Spec refers to as a non-completeness-preserving reduction step.)

> With my current understanding of 18.2.3 we cannot find
> a valid instantiation for K given the argument of raw type EntityKey.
> Even seeing sect. 18.5.5 (Unchecked Conversion Inference)
> I don't see how this how this can be leveraged from 18.2.3.

With some more advanced reduction logic, we could do something like

EntityKey -> α3
reduces to
EntityKey <: α3 *or* EntityKey<β> <: α3

(Handling the unchecked conversion as described in 18.5.5.)

But bound sets can't encode disjunctive logic ("or"), so inference is forced to make a greedy choice, and it generally prefers the left branch.  (As I suggested above when I mentioned 18.2.2, though, it may take the right branch if that's obviously necessary, as in "C -> C<α>".  Yes, I know it remains to specify what "obviously necessary" means.)


More information about the lambda-spec-experts mailing list