Many compile-time checks need to reason about types that are not known yet: the type
arguments of a generic method called without any, or the type argument of
new ArrayList<>(). Type inference works them out (JLS chapter 18).
It has three stages, which feed one another:
- Reduction takes a claim about an expression or type, called a constraint formula, and reduces it to bounds on inference variables.
- Incorporation keeps the bounds consistent as new ones arrive, and carries bounds between variables that depend on each other.
- Resolution looks at a variable’s bounds and picks an instantiation, deciding the order in which dependent variables are resolved.
Reduction can trigger incorporation, incorporation can lead to more reduction, and resolution can cause further incorporation. The rest of the page gives the rules; the Generics page shows inference from the programmer’s side.
Concepts and notation (18.1)
Inference variables (18.1.1)
Inference variables are placeholders for types, written with Greek letters such as α and β so as not to be confused with type variables. In this chapter “type” includes type-like syntax that mentions inference variables; a proper type mentions none.
Constraint formulas (18.1.2)
A constraint formula states a compatibility or subtyping fact that may mention inference variables:
| Formula | Meaning |
|---|---|
‹Expression → T› | The expression is compatible in a loose invocation context with T |
‹S → T› | Type S is compatible in a loose invocation context with T |
‹S <: T› | Reference type S is a subtype of reference type T |
‹S <= T› | Type argument S is contained by type argument T |
‹S = T› | Type S is the same as type T, or type argument S is the same as type argument T |
‹LambdaExpression →throws T› | The checked exceptions thrown by the lambda’s body are declared by the throws clause of T’s function type |
‹MethodReference →throws T› | The checked exceptions thrown by the referenced method are declared by the throws clause of T’s function type |
For example, Collections.singleton("hi") gives ‹"hi" → α›,
which reduces to ‹String <: α›. Arrays.asList(1, 2.0) gives
‹1 → α› and ‹2.0 → α›, which become ‹Integer <: α›
and ‹Double <: α›. List<Thread> lt = new ArrayList<>()
gives ‹ArrayList<α> → List<Thread>›, which becomes
‹α = Thread›.
Bounds (18.1.3)
| Bound | Meaning |
|---|---|
S = T | At least one is an inference variable; S is the same as T |
S <: T | At least one is an inference variable; S is a subtype of T |
false | No valid choice of inference variables exists |
G<α1, ..., αn> = capture(G<A1, ..., An>) | The variables stand for the result of capture conversion applied to the right-hand side, whose arguments may be wildcards and may mention inference variables |
throws α | α appears in a throws clause; informational only, it steers resolution away from checked exceptions |
With T a proper type, α = T makes T an instantiation of α;
α <: T makes it a proper upper bound and T <: α a
proper lower bound. Other bounds of the form S = T or
S <: T are dependencies. A set of bounds is a bound set;
the empty set is also called true.
Inference usually starts with a bound set made from the type parameters
P1, ..., Pp and fresh variables α1, ..., αp. A parameter with no bound gives
αl <: Object; otherwise each type T in its bound gives
αl <: T[P1:=α1, ..., Pp:=αp], with αl <: Object added if
that leaves no proper upper bound.
Reduction (18.2)
Reduction simplifies each constraint formula into a bound set and further formulas, until none remain. It is soundness-preserving, so any answer that satisfies the reduced form satisfies the original. It is not completeness-preserving: some expressions fail inference that would type-check if the type arguments were written out.
Expression compatibility (18.2.1)
‹Expression → T›: if T is proper, true or false by loose invocation
compatibility; otherwise a standalone expression of type S reduces to
‹S → T›. A poly expression depends on its form:
| Expression | Reduces to |
|---|---|
Parenthesised ( e ) | ‹e → T› |
| Class instance creation or method invocation | The bound set B3 of the inference for that invocation (18.5.2.1), which may add new variables and dependencies |
Conditional e1 ? e2 : e3 | ‹e2 → T› and ‹e3 → T› |
switch expression with results e1, ..., en | ‹ei → T› for each |
| Lambda or method reference | The rules below |
This is how nested generic calls are inferred together: the bounds of an inner
call are lifted into the outer one. List<String> ls = new
ArrayList<>(Collections.emptyList()); compiles because the inner variable is
tied to the outer by a dependency and resolved once the outer target is known.
A lambda, ‹LambdaExpression → T› with T mentioning a variable: false if T
is not a functional interface, or if no valid function type comes from the ground
target type T′ (15.27.3), or if the lambda is not congruent with it (different
parameter count; an implicitly typed lambda with non-proper parameter types; a
void result with a body that is neither a statement expression nor a
void-compatible block; a non-void result with a block that is not
value-compatible). Otherwise it reduces to all of these: for explicit parameter
types F1, ..., Fn against G1, ..., Gn, ‹Fi = Gi› and ‹T′ <: T›;
and for a return type R, assuming the lambda’s parameter types are the function
type’s, false if R is proper and a result expression is not assignment-compatible
with R, otherwise ‹e → R› for the expression body or for each result
expression. No bounds come from the function type’s throws clause at this
stage.
A method reference, ‹MethodReference → T›: false if T is not a
functional interface with a function type, or if no method is potentially
applicable. An exact method reference reduces its parameters to
‹Pi → Fi› (with P1 as the receiver via ‹P1 <: ReferenceType›
when n = k + 1) and its result to ‹R′ → R›, where R′ is the captured return
type; false if R is non-void and the method is void. An inexact one
requires proper parameter types, finds the compile-time declaration (15.13.1), and
reduces its return type, lifting a generic method’s bounds as above where the type
arguments are elided.
Type compatibility (18.2.2)
‹S → T› is decided in order:
- Both proper: true if S is compatible with T in a loose invocation context, else false.
- S primitive: box it to S′ and reduce
‹S′ → T›. - T primitive: box it to T′ and reduce
‹S = T′›. - T is parameterised
G<T1, ..., Tn>and no supertype of S has the formG<...>but rawGis a supertype of S: true (unchecked conversion). - The same for an array type
G<T1, ..., Tn>[]k. - Otherwise
‹S <: T›.
Subtyping and containment (18.2.3)
‹S <: T›: if both are proper, true if S is a subtype of T. If S is the
null type, true; if T is the null type, false. If S is a variable α, the bound
α <: T; if T is a variable α, the bound S <: α. Otherwise by
the form of T:
- Parameterised class or interface, or an inner class of one, with type arguments
A1, ..., An: find the corresponding supertype of S with arguments B1, ..., Bn, or
false if there is none, and reduce
‹Bi <= Ai›for each. - Any other class or interface type: true if it is among S’s supertypes.
- Array type
T′[]: find the most specific array supertypeS′[]of S, or false. If neither is primitive,‹S′ <: T′›; otherwise true only if they are the same primitive type. - Type variable: true if S is an intersection type containing T; otherwise, if T
has a lower bound B,
‹S <: B›; otherwise false. - Intersection type
I1 & ... & In:‹S <: Ii›for each.
‹S <= T›, for type arguments:
| T | S is a type | S is ? | S is ? extends S′ | S is ? super S′ |
|---|---|---|---|---|
| A type | ‹S = T› | false | ||
? | true | |||
? extends T′ | ‹S <: T′› | ‹Object <: T′› | ‹S′ <: T′› | ‹Object = T′› |
? super T′ | ‹T′ <: S› | false | ‹T′ <: S′› | |
Type equality (18.2.4)
‹S = T› for types: true or false if both are proper; false if either is
the null type; the bound α = T or S = α if one is a variable and
the other is not primitive. Class or interface types with the same erasure reduce
to ‹Bi = Ai› for their type arguments; array types reduce to
‹S′ = T′›; intersection types pair each element with its counterpart.
Anything else is false.
For type arguments: both types, as above; both ?, true; ?
against ? extends T′, ‹Object = T′› (and the reverse);
? extends S′ against ? extends T′ and ? super S′
against ? super T′, ‹S′ = T′›; anything else false.
Checked exceptions (18.2.5)
‹LambdaExpression →throws T› and
‹MethodReference →throws T›: let E1, ..., En be the non-proper types in
the function type’s throws clause, and X1, ..., Xm the checked exceptions
the body or referenced method can throw. If n = 0, the result is false if some Xi is
not a subtype of a proper thrown type, and otherwise true. If n > 0, each Xi that
is not a subtype of a proper thrown type gives ‹Xi <: Ej› for every j, and
every Ej gets the bound throws Ej. These are kept apart from return types
because return types affect which method is applicable, and thrown exceptions must
not.
Incorporation (18.3)
As bounds accumulate, new ones are implied. Incorporation adds them until nothing
changes. At that fixed point, if false is absent, every proper lower bound
of a variable is a subtype of every proper upper bound, and a dependency
α = β or α <: β carries the bounds of one variable to the
other.
Complementary pairs (18.3.1)
| Bounds | Imply |
|---|---|
α = S and α = T | ‹S = T› |
α = S and α <: T | ‹S <: T› |
α = S and T <: α | ‹T <: S› |
S <: α and α <: T | ‹S <: T› |
α = U (U proper) and S = T | ‹S[α:=U] = T[α:=U]› |
α = U (U proper) and S <: T | ‹S[α:=U] <: T[α:=U]› |
α <: S and α <: T, where S has a supertype G<S1, ..., Sn> and T has a supertype G<T1, ..., Tn> | ‹Si = Ti› for each pair of type arguments that are types, not wildcards |
Bounds involving capture (18.3.2)
A bound G<α1, ..., αn> = capture(G<A1, ..., An>) implies bounds on
the variables from the declared bounds of G’s parameters. Let Bi be the bound of
the i-th parameter, θ the substitution of the variables for the parameters, and R a
type that is not a variable. Then for each i:
| Ai | αi = R | αi <: R | R <: αi |
|---|---|---|---|
| Not a wildcard | The bound αi = Ai is implied | ||
? | false | ‹Bi θ <: R› | false |
? extends T | false | ‹T <: R› if Bi is Object; ‹Bi θ <: R› if T is Object | false |
? super T | false | ‹Bi θ <: R› | ‹R <: T› |
Resolution (18.4)
Resolution gives variables their instantiations. Dependencies decide the order:
given a bound α = T, α <: T, T = α or
T <: α where T is or mentions β, α depends on the resolution of β,
unless α is on the left of a capture bound, in which case β depends on α. A variable
on the left of a capture bound depends on every other variable in that bound. A
variable depends on itself, and dependencies are transitive.
To resolve a set of variables, take the set V of those and everything they depend on. While V has uninstantiated variables, pick the smallest non-empty set { α1, ..., αn } of them that depends on nothing outside itself except variables that already have instantiations, then:
- Unless a capture bound names one of them, form a candidate for each: the
lubof its proper lower bounds if it has any; otherwise, if the bound set hasthrows αiand every proper upper bound is a supertype ofRuntimeException,RuntimeException; otherwise theglbof its proper upper bounds. Incorporate these. If the result has nofalse, go round again. - Otherwise, or if a capture bound was involved, make fresh type variables Y1, ...,
Yn: Yi has lower bound
lubof αi’s proper lower bounds (if any) and upper boundglb(U1 θ, ..., Uk θ)with θ = [α1:=Y1, ..., αn:=Yn]. If they are not well formed, resolution fails. Otherwise remove the capture bounds on these variables and incorporateαi = Yi. If that givesfalse, resolution fails.
The second method is modelled on capture conversion. It is the fallback when a variable’s own bounds produce an inconsistent instantiation.
Uses of inference (18.5)
Invocation applicability inference (18.5.1)
To test whether a potentially applicable generic method m applies to an invocation
with no explicit type arguments: let θ map the type parameters to fresh variables.
B0 is the bound set from their declared bounds, with throws αi added for
each parameter in m’s throws clause, giving B1. A constraint set C comes
from the formal parameter types F1, ..., Fn and the arguments e1, ..., ek:
| Phase | Rule |
|---|---|
| Strict invocation | Not applicable if k ≠ n, or if an argument pertinent to applicability is a standalone primitive and Fi a reference type, or Fi primitive and the argument not a standalone primitive. Otherwise ‹ei → Fi θ› for each pertinent argument |
| Loose invocation | Not applicable if k ≠ n. Otherwise ‹ei → Fi θ› for each pertinent argument |
| Variable arity invocation | ‹ei → F′i θ› for each pertinent argument, with F′1, ..., F′k the first k variable arity parameter types |
C is reduced and incorporated with B1 to give B2. The method is applicable if B2
has no false and all its variables can be resolved. That resolution only
shows that an answer exists; its results are not kept.
List<Number> ln = Arrays.asList(1, 2.0); // public static <T> List<T> asList(T... a)
// B2 after reducing the arguments: { α <: Object, Integer <: α, Double <: α }
// The method is applicable, since those bounds resolve to
// α = Number & Comparable<? extends Number & Comparable<?>>
// With the target type, B3 adds α = Number, and the result is List<Number>
Invocation type inference (18.5.2)
Several rounds of inference are needed, so that the target type can affect the type of an invocation without affecting which method is chosen.
Poly method invocations (18.5.2.1). For a target T, B3 is derived from
B2. Let R be m’s return type. If unchecked conversion was needed, reduce
‹|R| → T›. If R θ is a wildcard-parameterised
G<A1, ..., An>, use fresh variables β1, ..., βn with
‹G<β1, ..., βn> → T› and the bound
G<β1, ..., βn> = capture(G<A1, ..., An>). If R θ is a variable α
and T is a non-wildcard reference type with wildcard-parameterised or conflicting
lower bounds on α, or T is a parameterisation of G where α’s bound makes it raw, or
T is primitive and a wrapper class is an instantiation, upper or lower bound of α:
resolve α and reduce ‹U → T› with U the capture of its instantiation.
Otherwise reduce ‹R θ → T›. The invocation is compatible with T if B3 has
no false and resolves.
Additional argument constraints (18.5.2.2). The set C holds, for each
argument ei not pertinent to applicability, ‹ei → Fi θ›, and, by the form of
ei: for a lambda, ‹ei →throws Fi θ› with the search applied to the lambda
body and its result expressions; for a method reference,
‹ei →throws Fi θ›; for a poly creation or invocation, that inner
inference’s own C; for parenthesised, conditional and switch expressions,
the same rules on their parts.
While C is not empty, a subset is chosen in which no constraint’s input variable can
influence the output variable of another. If none exists, there is a cycle: from the
constraints in it that depend on nothing outside it, take one whose expression is
furthest left. The chosen input variables are resolved, the substitution is applied
to every constraint, and the result is reduced and incorporated, producing B4. If B4
has no false, its variables are resolved to T1, ..., Tp, and the invocation
type is m’s type with θ′ = [P1:=T1, ..., Pp:=Tp] applied, or with the return and thrown
types erased if unchecked conversion was needed. If B4 has false or
resolution fails, it is a compile-time error.
| Formula | Input variables |
|---|---|
‹LambdaExpression → T› | T itself if it is a variable; if T is a functional interface, the variables in its parameter types when the lambda is implicitly typed, and the input variables of ‹e → R› for each result expression |
‹LambdaExpression →throws T› | T itself if a variable; if functional, the variables in its parameter types when implicitly typed, and those in the return type |
‹MethodReference → T› | T itself if a variable; for an inexact reference, the variables in T’s parameter types |
‹MethodReference →throws T› | T itself if a variable; for an inexact reference, the variables in its parameter and return types |
Parenthesised, conditional and switch forms of ‹Expression → T› | The input variables of the contained expressions |
| Everything else | None |
The output variables of a constraint are the variables in T that are not input variables.
Functional interface parameterisation (18.5.3)
When a lambda with explicit parameter types P1, ..., Pn targets a wildcard-parameterised
F<A1, ..., Am>, take the function type of F<α1, ..., αm> with
parameter types Q1, ..., Qk. If n ≠ k there is no parameterisation. Otherwise reduce
‹Pi = Qi› to a bound set B; false means none. Each A′i is B’s
instantiation of αi if it has one, otherwise Ai. The result must be well-formed and
a subtype of the original; wildcards left over are replaced as described for the
function type. So
Predicate<? super Integer> p = (Number n) -> n.equals(23); makes
Predicate<Number>.
More specific method inference (18.5.4)
To test whether m1 is more specific than a generic m2 for arguments e1, ..., ek, let S1, ..., Sk be m1’s parameter types (its own type parameters stay as type variables) and T1, ..., Tk be m2’s after θ. Start from m2’s bound set, then for each i:
- If Ti is proper: true if Si is more specific than Ti for ei, otherwise false.
- If they are not both functional interface types, or one interface is a
superinterface or subinterface of the other:
‹Si <: Ti›. - Otherwise compare their function types: same type parameters and parameter
count, equal bounds and parameter types, and return types compared as follows.
For an explicitly typed lambda or an exact method reference, a
voidRT is true; a primitive RS against a reference RT is true when every result is a standalone primitive (and the reverse with reference results); otherwise‹RS <: RT θ′›. Parenthesised, conditional andswitcharguments are handled through their parts.
For a variable arity comparison a final ‹Sk+1 <: Tk+1› is added. Reducing
and incorporating everything with m2’s bound set must give no false and
resolve, for m1 to be more specific.
Record pattern inference (18.5.5)
A record pattern for a generic record R that names no type arguments has them inferred from the type T being matched. T must be checked-cast convertible to raw R. Variables α1, ..., αn stand for R’s parameters, with their declared bounds in B0. T′ is derived from T: for a parameterised T, its capture with each capture variable replaced by a fresh variable β carrying its bounds; T unchanged for any other class or interface type. For a type variable or intersection type, the rule applies to each bound.
If T′ is a parameterisation of G and R<α1, ..., αn> has a supertype R′
that is one too, ‹T′ = R′› is reduced and incorporated. With no
false, the variables are resolved, skipping the lower-bound step so that
fresh type variables are introduced directly; the type of the pattern is the upward
projection of the result. For op instanceof Mapper(var in, var out) with
op a UnaryOperator<? extends CharSequence>, this gives
Mapper<? extends CharSequence>.