Reader Mail Bag -- Local Type Inference

Brian Goetz brian.goetz at
Thu Mar 24 19:44:13 UTC 2016

(Please do not respond directly to this mail; if you want to respond, 
please use the follow-up survey.)

The first survey is closed, and we received ~2500 entries, with over 400 
providing long-form comments and questions.  I'll try to answer some of 
the the constructive questions here, which I've divided into groups by 
topic.  I've answered the mostly non-subjective questions here; I'll 
cover the more subjective ones in a separate mail.  (Also see the 
earlier "Reader Mail Bag" response, which answers some of the 
early-arriving questions.)

"How does it work"

Q: How does mutability/finality work in "var only"?

In all of the alternatives, effective-finality analysis applies to local 
variables declared with 'var'.  In the alternatives that also include 
val/let, these are treated as explicitly final, but the existence of 
val/let doesn't mean that we don't do effectively-final analysis on 
variables declared with 'var'.

Q: Are 'val' and 'final var' equivalent?

Yes, val/let would be equivalent to 'final var'.

Q: Will this break existing programs that use 'var' or 'val' as field or 
method names?


Q: Will this break existing programs that use 'var' or 'val' as a class 
or interface name?

Yes.  We've explored how to provide a means to save these classes, at 
the cost of additional complexity, but the return-on-complexity didn't 
seem to be there.

Q: Does 'let' mean 'immutable'?

It means 'final'.

Q: Are these dynamically typed?

No!  This is not dynamic typing; it is allowing the compiler to 
determine the static type for you, rather than making you provide it 
explicitly.  Once the compiler chooses a type (which is based entirely 
on the type of the initializer expression), it is as if you had provided 
that type explicitly.

Q: How does this interact with existing type inference, such as for 
diamond constructors, generic methods, or lambda formals in the 
initializer expression?

For inference variables in the initializer expression, if they can be 
inferred from type information in the initializer expression itself, 
then the expression is compatible with using 'var' on the declaration.  
On the other hand, if they cannot be inferred without a target type, and 
no target type is provided, then inference will fail, and the compiler 
will issue an error message.

Q: What is the type of an anonymous class creation expression?

For an anonymous class creation expression, we infer the base type.  So 
for example, in:

     var foo = new Foo() { ... }

we would infer 'Foo' for the type of 'foo'.

Q: How does it work with variables that don't have initializers.

It doesn't; this is an error.

Q: Can I mix explicit and implicit types?


Tooling Impact

Q: Does this affect the runtime or classfile?


Q: Will this affect runtime performance?


Q: Will this affect compiler performance?

In any realistic case, no.  (Even with a manifest type, the compiler 
still has to synthesize the type of the initializer, and perform a 
subtyping check against the manifest type.)

Q: Does this affect Javadoc?

No; local variables do not appear in Javadoc.

Q: Can we have Javac modify the code and insert the type?

IDEs will certainly do this for you.  Javac doesn't modify code 
in-place, but we are considering enhancing features of javac like 
-printSource to expand inferred types (and not just for locals, but also 
for generic methods, diamond constructors, and lambda formals), as a 
debugging aid.

Q: Can we have a compiler warning that detects the use of 'var'?

This seems better handled by style-checking tools like CheckStyle.

Q: Will it require targeting the latest class file?

There are no classfile changes mandated by this feature.

Feature Scope

Q: Why not allow it on fields or method returns?

Because these are part of the *interface* of your class, and these 
appear in classfiles (which could then cause silent compatibility issues 
if implementation changes cause the inferred type to change.)  Locals 
cannot be accessed outside of the declaring scope; their types are pure 
implementation details.

Q: OK, what about private fields or method returns?  They aren't 
accessible from other classes.

We could choose to include these; this would be a tradeoff of greater 
complexity (more complex set of rules for when 'var' is allowed, would 
mean broadening the accessibility of a member becomes potentially not a 
source-compatible change) for greater applicability.  We're inclined to 
keep it simple.

Q: Why not allow diamond on the LHS?

This is a viable feature, and has pros and cons.  This helps for generic 
types, but doesn't help at all for long type names 

Q: How does it interact with subsequent uses?

As proposed, we infer the type of the local purely from the initializer 
type; thereafter, the type is fixed as if it were explicitly provided.  
Alternately, we could instead have chosen to infer it from all 
assignments; this might well result in inferring weaker types (like 
Object).  The choice to use the initializer only is a tradeoff; we gain 
greater simplicity and stability, at the cost of being able to infer 
types for more locals.  (Because other assignments might be far away 
from the declaration, using all assignments could easily generate 
confusing action-at-a-distance errors.)

More information about the platform-jep-discuss mailing list