Type Inference
Here is a step-by-step guide on how to deal with type inference.
Summary of Steps
For type inference, there are 3 main things to check -- Target typing, argument typing and the type parameter bound declared.
Steps
The steps will be clearer with an actual example but here are the main points
1 2 | |
- Target typing:
- Check the return type of the method,
T - Check type of variable assigned to,
varType. If none, move on to other steps. - Here,
T<:varType
- Check the return type of the method,
- Argument typing:
- Check the parameter type of the method, usually
<? extends T>/<? super T> - Check the type of argument passed in,
argType - If
paramTypeis<? extends T>,argType<:T - If
paramTypeis<? super T>,T<:argType
- Check the parameter type of the method, usually
- Type parameter bound:
- Check the declared type for the generic type (between
<>) - Here,
T<:boundType
- Check the declared type for the generic type (between
After getting all the constraints, compare the bounds and choose the most specific bound. If one cannot be determined e.g. T <: ColoredCircle & Circle <: T, we get an error.
Subtypes
Remember the compiler does not consider any types not found from the above analysis. If you have Circle <: T <: getAreable, T is inferred as Circle and not ColoredCircle.
The idea behind this is Circle can have many subclasses -- TransparentCircle, SpinningCircle etc., the compiler won't be able to determine which it is or even be aware of all the subtypes.
Step-by-step
Example 1
We will look at the following example from the notes where a type is successfully inferred.
1 2 | |
| Target type | Argument type | Type parameter |
|---|---|---|
Target type: From the code snippet, return type of the method is T and type of the variable assigned to is GetAreable
- Thus, we get
T<:GetAreable
| Target type | Argument type | Type parameter |
|---|---|---|
T <: GetAreable |
Argument type: From the code snippet, type of the parameter is Seq<? super T> and type of argument passed in is Seq<Circle>
- Based on subtyping,
Seq<Circle><:Seq<? super T> - Thus, we get
T<:Circle
Deriving T <: Circle
<? super T>is a lower-bounded wildcard bounded byT- We are given
Seq<Circle><:Seq<? super T> - For 2 to hold,
Circle<:? super T - We need
Circleto be a supertype ofT - Thus,
T<:Circle
| Target type | Argument type | Type parameter |
|---|---|---|
T <: GetAreable |
T <: Circle |
Type parameter bound: From the code snipppet, we can see the type declaration <T extends Circle>
- Thus, we get
T<:Circle
| Target type | Argument type | Type parameter |
|---|---|---|
T <: GetAreable |
T <: Circle |
T <: Circle |
Given the above bounds, we can determine the most specific bound is T <: Circle (since Circle <: GetAreable), thus T is inferred as Circle.
Example 2
Here is another example
1 2 | |
| Target type | Argument type | Type parameter |
|---|---|---|
Target type: From code snippet, there is no variable assigned to, so we move on
| Target type | Argument type | Type parameter |
|---|---|---|
| - |
Argument type: From code snippet, type of the parameter is Seq<? extends T> and type of argument passed in is Seq<ColoredCircle>
- Based on subtyping,
Seq<ColoredCircle><:Seq<? extends T> - Thus, we get
ColoredCircle<:T
Deriving ColoredCircle <: T
<? extends T>is a upper-bounded wildcard bounded byT- We are given
Seq<ColoredCircle><:Seq<? extends T> - For 2 to hold,
ColoredCircle<:? extends T - We need
ColoredCircleto be a subtype ofT - Thus,
ColoredCircle<:T
| Target type | Argument type | Type parameter |
|---|---|---|
| - | ColoredCircle <: T |
Type parameter bound: From the code snipppet, we can see the type declaration <T extends Circle>
- Thus, we get
T<:Circle
| Target type | Argument type | Type parameter |
|---|---|---|
| - | ColoredCircle <: T |
T <: Circle |
Given the above bounds, we get ColoredCircle <: T <: Circle so the most specific bound is ColoredCircle <: T. Thus, T is inferred as ColoredCircle.
Failure
This is an example from the notes where the bound cannot be determined.
1 2 | |
| Target type | Argument type | Type parameter |
|---|---|---|
Target type: From the code snippet, return type of method is T and type of the variable assigned to is ColoredCircle
- Thus, we get
T<:ColoredCircle
| Target type | Argument type | Type parameter |
|---|---|---|
T <: ColoredCircle |
Argument type: From code snippet, type of the parameter is Seq<? extends T> and type of argument passed in is Seq<GetAreable>
- Based on subtyping,
Seq<GetAreable><:Seq<? extends T> - Thus, we get
GetAreable<:T
Deriving GetAreable <: T
<? extends T>is a upper-bounded wildcard bounded byT- We are given
Seq<GetAreable><:Seq<? extends T> - For 2 to hold,
GetAreable<:? extends T - We need
GetAreableto be a subtype ofT - Thus,
GetAreable<:T
| Target type | Argument type | Type parameter |
|---|---|---|
T <: ColoredCircle |
GetAreable <: T |
Type parameter bound: From the code snipppet, we can see the type declaration <T extends Circle>
- Thus, we get
T<:Circle
| Target type | Argument type | Type parameter |
|---|---|---|
T <: ColoredCircle |
GetAreable <: T |
T <: Circle |
Given the above bounds, we get T <: ColoredCircle (and also <: Circle) and GetAreable <: T.
However, since ColoredCircle <: Circle <: GetAreable, T cannot be both a subtype of Circle and supertype of GetAreable. Thus, the compiler cannot infer a bound and an error is thrown.