
JEP 286: Local-Variable Type Inference - azth
http://openjdk.java.net/jeps/286
======
scalesolved
I hope this gets added to the language, we use Lombok heavily where I work and
find it pretty great with it's val class that allows type inference, although
it requires an explicit import and can't be used within Lambdas.

The website isn't the prettiest but Lombok is a fantastic addition to a Java
codebase and massively reduces the amount of boiler plate often associated
with Java. A list of Lombok features
[https://projectlombok.org/features/index.html](https://projectlombok.org/features/index.html)

Java 8 + Lombok + Guava == happy developers (well at least in my case :) ).

~~~
biehl
My experience is similar, however more like: Groovy === happy developers

~~~
mindcrime
Same here. Groovy rocks my world and is the main reason I've barely even
looked at Java 8.

~~~
incepted
If Groovy rocks your world, wait until you meet Kotlin...

~~~
skippytheroo
If Kotlin rocks your world, wait until you meet the Scala.

~~~
incepted
I met Scala a few years ago, we had some fun together but she's way too high
maintenance.

------
freekh
I hope this goes through!

A while back, I was trying to explain in what ways Scala is more typesafe than
Java for a friend (which was one argument to why I prefer Scala so much), and
ended up realizing how important type inference is in this regard.

The reason was this: quite often type signatures correctly modelling a domain
ends up being pretty hard to read. In a good and proper API, this happens in
particular whenever something is composable and is, in fact, composed (if one
can say that). I suppose one can say that, for Scala, it took a bit of time
before people figured out how to do this properly, rather than just hacking
the type system, but I feel that the ecosystem is more or less there now...
YMMV though...

As an example: in something like Slick (a functional-relational mapper) in
Scala you end up with monads to model your SQL query. One advantage this has
is compile errors whenever you're doing something wrong (i.e. more typesafe),
but the types that a SQL query representation ends up with will necessarily be
pretty hard to read and practically impossible to write (you could have a
Query of a long chain of other types for example if you're querying multiple
columns). In this example, somebody writing the query might not care about the
type of the query. They will care about what the query will end up returning
when executed though. I suppose it will lead to trouble when you can't read
the type signatures well enough and try to figure out the compiler error, but
I reckon it's better to have an error, than to see it fail runtime (at least
in my world :). It's easier and safer to do maintenance too, since the
compiler and IDEs can know how things should work.

In any case, you could conclude that, in a statically compiled language where
you do not have type inference, you'll end up trading in preciseness in the
model of the domain, in order to have readable code from the callsite. In
turn, this leads to less typesafe code.

One challenge that Java will have even when implementing this though, is how
to move the entire ecosystem to something that is indeed more typesafe. This
only works if the APIs are doing this right.

EDIT: typos, language and less parens

~~~
incepted
> A while back, I was trying to explain in what ways Scala is more typesafe
> than Java for a friend (which was one argument to why I prefer Scala so
> much), and ended up realizing how important type inference is in this
> regard.

Type inference has absolutely nothing to do with type safety. Programming
languages don't become more or less statically type safe depending on whether
they support type inference.

Type inference only enables to omit type annotations in certain parts of your
code. Your values are just as typed as if you had used that type annotation.

As for the original point you were trying to make, I would say Scala is no
more type safe than Java. They are about on par in how they allow unsafe
expressions to type check. Scala's type system is certainly richer than
Java's, but that's a separate point (and maybe the one you were trying to
make).

> In any case, you could conclude that, in a statically compiled language
> where you do not have type inference, you'll end up trading in preciseness
> in the model of the domain, in order to have readable code from the
> callsite. In turn, this leads to less typesafe code.

Absolutely not.

Omitting a type in your source doesn't remove the type from that variable. The
code is exactly as typed with that type annotation as it is without.

~~~
wtetzner
I think his argument is that without type inference, programmers will avoid
more complex types, and therefor model their domain with less precise types.

~~~
freekh
Yup :) The comment is valid though: you don't get more type safety because of
type inference. Thus saying it gets more typesafe is probably wrong...

~~~
usrusr
The curious case of the broken inverse: you won't get more type safety by
having type inference, but you are likely to end up with less type safety when
you are not having it ;)

~~~
incepted
I have seen no evidence of that. With that logic, Java programmers would use
"Object" instead of "List<String>" because they have fewer characters to type?

This makes no sense.

~~~
usrusr
It makes more sense after you have deliberately weakened the typing of an API
because you felt that you were overstretching your co-developers (and/or your
own) patience for nested angle brackets.

The basic idea is that type inference would shift the sweet spot for the right
amount of typing upwards.

------
plesner
In their list of risks there's one missing: it makes changing the return type
of a method to a subclass a subtly breaking change. Say some api defines a
method,

Collection<String> getNames() { ... }

and in your code you do

var names = getNames(); if (...) names = Collections.emptySet();

Now, if the api later changes getNames() to be

List<String> getNames() { ... }

your code breaks. That's one of the advantages of only allowing this on final
variables (besides reusing an existing keyword): it means this can't happen.
Of course you already have a problem if the class has subclasses but I would
argue that this is more subtle and problematic.

~~~
wvenable
I think in practice this would be pretty rare. At least coming from a C#
background it doesn't seem to be an issue I've ever encountered.

~~~
plesner
It's possible that it's more a theoretical than a practical problem, I would
hope so. But it does seem to me like fundamentally a problematic property of a
type system that having more accurate types can break your program. It's the
static type equivalent of breaking the Liskov substitution principle.

~~~
wvenable
If a library author makes such a fundamental change their public API by making
a method return more specific, I might actually want to know about it. I don't
find it particularly problematic since it seems like code smell for that sort
of change to happen in the first place.

------
vivin
This is pretty nice. Hope it goes through. It's about time that we had local-
variable inference in Java. It does mean that the diamond can't be used this
way:

    
    
      var list = new ArrayList<>();
    

There is no way to infer the type of the generic parameter. So we will go back
to doing:

    
    
      var list = new ArrayList<String>();
    

Which isn't a big deal IMO because the generic parameters had to be specified
on the LHS anyway to use the diamond. You also end up using fewer characters
in general with var even without being able to use the diamond.

Regarding val vs let, I prefer let simply because it's harder to accidentally
type let vs typing var instead of val (or vice versa).

~~~
kodablah
> Regarding val vs let

I like the approach mentioned of just reusing final for immutability.

~~~
cballard
Making the immutable version the same length as (or preferably, shorter than,
e.g. Rust) the mutable version encourages the use of immutability.

------
stygiansonic
The approach taken to preserve backwards compatibility with existing code that
already uses `var` as an identifier is interesting:

 _" The identifier var will not be made into a keyword; instead it will be a
reserved type name. This means that code that uses var as a variable, method,
or package name will not be affected; code that uses var as a class or
interface name will be affected (but these names violate the naming
conventions.)"_

I didn't know there were such things as "reserved type names", but I'm
guessing `var` will be treated sort of like `String` or `Object` (always
available without an explicit import) and maybe it'll be a class in
`java.lang`?

Seems like a much "nicer" approach than when Enum types were introduced with
the `enum` keyword, but I guess that was slightly different.

~~~
TazeTSchnitzel
PHP recently did the same thing with PHP 7, which introduced type declarations
for the scalar types. This meant we needed to reserve `int`, `float`, `string`
and `bool`. To minimise backwards-compatibility breakage, we only reserved
these as class/interface/trait names, and not in other contexts.

And it seems we were right to reduce the scope of it. PHP 7 prohibiting
"String" as a class name ended up being a significant BC break affecting
several projects. I imagine it might have been worse still if `string` was a
reserved word.

------
mmatants
I for one actually prefer to put a different type on the variable than the
implementation. I.e. in the given case, I would actually use:

`List<String> a = new ArrayList<String>();`

I know that for some things it is convenient, but I appreciate the reminder to
treat interface and implementation as distinct concepts.

~~~
involans
For local variables within the body of a method it doesn't really matter. As
long as the method signature is expressed in interfaces, you should be able to
use whatever implementations you want within the method body.

------
talideon
About time, given how long C# has had local type inference.

------
ryenus
Vote here:
[https://www.surveymonkey.com/r/KGPTHCG](https://www.surveymonkey.com/r/KGPTHCG)

This survey will remain open for one week, from Mar 9 to Mar 16.

------
andyfleming
This would definitely be a nice language feature to see in Java. It's one (of
many) of the reasons I'm attracted to Kotlin
([https://kotlinlang.org/](https://kotlinlang.org/)).

~~~
dloose
Ceylon ([http://ceylon-lang.org](http://ceylon-lang.org)) has it too! Sorry. I
just like Ceylon

~~~
nikolay
With all the fuzz behind Kotlin, just because JetBrains, I am surprised that
Ceylon has so little awareness! Ceylon, which has great features, multiple
compilation targets (can't wait for the LLVM backend), has a great entity
backing it (Red Had), and is created by a celebrity (at least in the Java
world) developer (Gavin King)!

~~~
ubertaco
Yeah, and it seems like a good project, but some downsides are that it has to
support multiple compilation targets (which can split a language into multiple
ecosystems), has only one major user (Red Hat), and was created by Gavin King

~~~
nikolay
Red Hat > JetBrains though - I doubt Kotlin has a much bigger user base. Plus,
being created by Gavin King is good, not bad. Also, multiple compilation
targets are considered a plus in other languages (like Dart) - why is this bad
for Ceylon? Especially when it along with Java it does not rely on native
libraries like most of the other languages like Python, Ruby, and Node.js - it
can be very useful and you don't need projects such as GWT if you can compile
Ceylon to JavaScript directly.

~~~
ubertaco
Kotlin's got more "noise" around it lately though, so I suspect adoption will
be quicker. Also, GMC is bigger than Red Hat, and while Telegram is smaller
for sure, it's got a decent amount of weight to it. Both use Kotlin (at least
according to the Kotlin homepage).

Gavin King is famous for three things in the Java world: creating the leaky-
abstraction-riddled ORM Hibernate that never quite behaves the way you want,
creating the never-really-used DI lib Seam that is notorious for being
difficult to test and configure (despite standardized "javax" APIs), and being
a difficult personality to work with.

Multiple compilation targets are a downside when: \- It means that as a user
of the language I have to write different code because the collections/scoping
on one target behave much differently than on another \- It means that as a
maintainer of the language, I have to try to make core language features work
with multiple very-different compilation targets, making the codebase N times
harder to maintain, and usually meaning either that the language stagnates for
long periods or that one platform is treated as "primary" and the others are
second-class citizens that trail behind (see Scala on the JVM vs Scala.js,
Clojure vs Clojurescript, F# vs Websharper, etc).

------
zZorgz
Having the option to use type inference in this specific use case is nice.

But I'm glad it's just for initializers.

After playing with Scala, I've found out I don't love type inference as much
as I thought I did. In Scala, the compiler can infer return types of named
procedures for example, but I've always found omitting these makes my code
harder to read, having to read the body (an implementation detail) to see what
it may be returning. It would be even worse if types were optional for
parameters (ala Haskell). Other modern languages (ala Rust) have come to
realization that this is a bad idea.

The same even applies to having the compiler infer types from local variables
to a lesser extent. A lot of my code goes through transformations and chains,
and sometimes it's not obvious what the resulting type is (and what the code
is doing) if the type is not specified.

Even looking at the _simple_ example mentioned from the article in isolation:

    
    
        var stream = list.stream();
    

I don't know what type stream is. Sure, I can make an educated guess that it's
some kind of Stream, but then knowing it's a parameterized, and then knowing
what kind of type it takes is even more challenging. I would have to look at
the right hand side (again, an implementation detail), or may have to traverse
up even further (eg: looking up how `list` is derived in this case) to figure
out for sure. Or worse yet, look up documentation. Imagine if the right hand
side was composed by calling a series of procedures, then you have to traverse
all the way to the right to validate the type. In the end, you have a mental
burden.

The same issue applies with calling functions and assiging their result to a
local variable you're initializing.

I'm happy to see type inference is just added for initializers. Whilst I
haven't found writing 'Foo foo = new Foo()' that common in functional code, I
see that being prevalent in Java at least. On the flip side though, if your
language or use case (C++?) makes code harder to understand rather than easier
by specifying types, then you may be wishing you had type inference :)

~~~
dllthomas
> It would be even worse if types were optional for parameters (ala Haskell).

The Haskell community seems pretty well agreed that top level declarations
should have type annotations (arguments and return values) in code that's
going to be maintained. With -Wall, GHC will even give you warnings about top
level declarations without types. No reason to require it for quick (~5 min)
experimental hackary or one-off scripting - in that case, add it if it helps
and omit it if it doesn't, and the ability to ask what the compiler thinks the
type is can come in handy as well.

~~~
zZorgz
Seems reasonable. But now you risk the chance of reading code written by those
who don't specify types, don't know better, or don't use -Wall.

I think deciding what to allow and prohibit by default is an important design
decision.

~~~
dllthomas
> I think deciding what to allow and prohibit by default is an important
> design decision.

I don't think I disagree with that, but I don't think it was made wrong here.
It's true that you "risk the chance" of reading unannotated code, but I find
there are times when the code I need to write is clearer to me than the type
it'll have, and being able to defer annotation past compilation is
occasionally a big win and often a small one.

And in that worst case scenario of having to look at unannotated code, you can
fall back to asking your tooling.

~~~
zZorgz
My own experience is that I've been running into less scenarios where I think
omitting the type makes code more readable (coming from Scala). It doesn't
help myself and my coworkers that it's easy (or 'lazy') to omit annotations.

I often wish I was using a text editor or IDE that understood my code and
could insert type annotations for me, maybe automatically, and maybe not
choose to do so if the line of code would "look" more redundant (i.e: parsing
variable name or constructor/initializer).

Rust is one language that, while has the capability of doing full type
inference, deliberately chose not to ([https://doc.rust-
lang.org/book/functions.html](https://doc.rust-lang.org/book/functions.html)):

> This is a deliberate design decision. While full-program inference is
> possible, languages which have it, like Haskell, often suggest that
> documenting your types explicitly is a best-practice. We agree that forcing
> functions to declare types while allowing for inference inside of function
> bodies is a wonderful sweet spot between full inference and no inference

~~~
dllthomas
I agree that omitting the type rarely, if ever, makes the code more readable.

But readability can _easily_ matter less than other things _over the course of
one compile_. This is why I think the best choice is demanding top level
annotations for reasonably complete code (say, every commit) and not doing so
before I can ask for the shape of something, or see that it outputs what I
thought, or run my tests, or see what type errors I get elsewhere.

It might be marginally better to demand it by default and have a flag to
disable the check. Demanding it always is more than marginally worse (which is
not to say it's the end of the world - it's totally not).

~~~
zZorgz
Yeah, when the effort put into inserting types is higher cost than reading
them (as with short lived / unstablized code), what you're saying makes sense.

Which is kind of why I wish for tooling outside the compiler to make the
effort in specifying types (when it increases readability, maybe based on
heuristics) very minimal. Wishful thinking.

~~~
dllthomas
Well, the warning from ghc gives you the inferred type, which can be helpful.
Copying it in place blindly is not advised - it won't break, but something
more specific might be better. And of course the compiler has no way of
getting inferring what choice of type aliases will be most (correctly)
communicative.

------
gabriele
I expect this breaking a few things in fields injection (a la Spring
@Autowired[1]) if implemented.

[1] [https://docs.spring.io/spring-
framework/docs/current/spring-...](https://docs.spring.io/spring-
framework/docs/current/spring-framework-reference/html/beans.html#beans-
autowired-annotation)

~~~
ygra
How would type inference for local variables mess with fields?

~~~
gabriele
according to Spring's documentation[1]:

 _@Autowired is fundamentally about type-driven injection_

If you replace:

    
    
        @Autowired
        FooDao fooDao;
    

with something like:

    
    
        @Autowired
        var fooDao;
    

I'm pretty sure type-driven injection breaks.

[1] [https://docs.spring.io/spring/docs/current/spring-
framework-...](https://docs.spring.io/spring/docs/current/spring-framework-
reference/html/beans.html#beans-autowired-annotation-qualifiers)

~~~
radq
The proposal only applies to local variables with non-null initializers. I'm
pretty sure you can't autowire local variables with Spring, and they don't
have initializers anyway.

------
nsxwolf
I've never liked this language feature very much. I'm the one that's doing the
inference when I read the code.

~~~
wmu
I totally agree, type inference speed writing a code, nothing more.

For easy cases reading such code is OK (var path = "/usr/bin/whatever" \-
obvious), but with a more complex code it becomes awful.

I'm a C++ programmer, and quite often I have to dig through dozens of headers
to find out what this magic "auto" really means.

~~~
icebraining
That's because C++ combines type inference with a gnarly compilation process.
In a language like Java, editors will support it and you'll just be able to
"mouse over" (or press a key combo in Vim/Emacs) and it'll tell you the
inferred type.

~~~
wmu
A good point, I didn't consider IDE support.

------
emmanueloga_
I wonder if this JEP draw any inspiration of
[http://www.eclipse.org/xtend/](http://www.eclipse.org/xtend/). For those new
to xtend, think of it as xtend is to java like coffeescript is to javascript.

I wish most of the xtend enhancements were integrated to java asap!

~~~
betimsl
Came to write this here. Xtend is amazing for these things.

------
acjohnson55
The reaction here is indicative of why I'm starting to consider whether maybe
it's best that Java stays Java. Sure, I'd personally be really annoyed typing
super redundant type declarations everywhere. But then again, I wouldn't have
to, because I'd simply use Scala, because it's more expressive. But a lot of
people clearly love good old verbose Java.

Java has been super slow to adopt things that are more or less the norm in
other popular languages. Why bother at this point? Isn't Kotlin a pretty good
sugared-up Java, anyway?

Lightbend (caretakers of Scala, Play Framework, Akka, Spark, Slick, SBT, and
others) wisely announced their dedication to both Scala and Java. It's caused
a big stir in the community, but it kind of seems like that should make
everybody happy. They're two ecosystems geared to serious software, built on a
solid platform, but with very different constituencies. Lightbend realized
there's no point in pressuring people to adopt Scala. People will do it on
their own, if they want to. But with strong support of both languages, they
can address a very broad swath of the development world.

~~~
skippytheroo
The reaction from some reminds me that some Java programmers will always
resist change. Java's current feature set is the one true way (tm) and any
features other languages have are bad, that is until they are officially added
to Java and are then suddenly considered part of the one true way (tm) and
further evidence of the language designers' wisdom.

------
jkot
I hope they will give it second though. In Java 'var' or 'val' are not
reserved keywords. It was difficult to migrate existing code when 'enum' was
introduced. 'const' is reserved but nothing for variable, perhaps 'new' or
some symbols.

~~~
ygra
They will be made into reserved _type names_. Since classes with those names
violate naming conventions anyway they should be quite rare.

------
pgroves
I really want this. I gave up on Ocaml because it just isn't a big enough
ecosystem but everything else seems like I'm making the wrong things explicit
in the code.

------
paramk
Scala ?

~~~
greydius
All we need is higher kinded types and implicits.

~~~
incepted
Higher Kinded Types, yes, but implicits... Yuck. Scala has definitely proven
them as a failed experiment, I hope we will never seem them again in languages
going forward.

~~~
acjohnson55
Implicits are tricky, but they are the best way I know of to solve the
problems they do. Other languages (JavaScript, Swift, etc.) have essentially
open classes, where attachment of additional functionality to a type impacts a
program globally. Implicits make it much easier to scope these extensions.

They are also quite useful for wiring up context (take Akka's ActorSystem, for
example) at the declaration level, so that the bodies of your classes and
functions only explicitly talk about your domain objects, rather than
framework machinery.

They're also a big part of the power behind projects like Shapeless, which are
quite useful for scrapping boilerplate.

Most controversially, they are also used to implement implicit type
conversions. Modern idiomatic Scala discourages this, but it can sometimes be
helpful.

------
exabrial
No thanks. I'll take the extra keystrokes

------
merb
var + val would be great.

~~~
nikolay
Yeah, let's spread the nonsense further! There are constants and variables;
values are something else. Just because "val" is a short abbreviation
everybody gets, it doesn't mean we should abuse it!

~~~
Others
I disagree with that sentiment. Constant to me implies decided at compile
time. The reason we should use "val" is because it is a short abbreviation
everybody gets. It also has symmetry with "var," as "value" and "variable" are
both adjectives, whereas "let" is a verb.

~~~
Retra
I've been programming for 15 years and I'd have no clue what the difference
between val and var is without looking it up. So I guess your definition of
'everybody' is JavaScript programmers?

And every time I see 'var', I cringe because I don't know if they mean
variable or variant. But 'val' is just usless. Variables evaluate to values,
constants evaluate to values, expressions are values, first-class functions
are values... Everything is val.

------
vbezhenar
Java loses its explicitness with each release. And it's still much worse as a
language than Scala, Kotlin, Groovy, Ceylon (and probably always will be
behind). I don't see much value in those improvements. If one wants a better
Java, he can use it right now. What Java needs is JVM improvements, like value
types or full support for bytecode hot reload. Java 1.4 was very minimalistic
and beautiful language. I liked it that way.

~~~
vorg
> it's still much worse as a language than Scala, Kotlin, Groovy, Ceylon

Some of those languages are better than each other, and they are much worse
languages than Clojure, another JVM-hosted language you ignored.

~~~
vbezhenar
Clojure is very different language, and I don't know it well, so I didn't
include it. Of course it's not a closed list.

------
linuxhansl
Hmm... I agree it looks nicer.

Who's typing Java code all by hand these days, though? There various IDEs that
save me all the typing.

It's also nice to be able to go the declaration of a variable to see exactly
what type it is - that's mostly for code not written by myself, or old code
that I do not remember. For local variable that's fine (I hope we won't infer
types this way for method parameters), so I guess I like it there.

~~~
the_af
> _Who 's typing Java code all by hand these days, though? There various IDEs
> that save me all the typing._

Reducing boilerplate mostly helps with _reading_ code, not with writing it.

~~~
agumonkey
It does, my code and trail of thought is significantly different when
switching to coffeescript from javascript.

------
phaed
Oh god yes.

------
PantaloonFlames
Welcome to 2007?

C# added implicitly typed local variables (and the var statement) to the
language 9 years ago.

[https://msdn.microsoft.com/en-
us/library/bb308966.aspx](https://msdn.microsoft.com/en-
us/library/bb308966.aspx)

~~~
jonsterling
Welcome to 1972, actually!
[https://en.wikipedia.org/wiki/Logic_for_Computable_Functions](https://en.wikipedia.org/wiki/Logic_for_Computable_Functions)

------
djsumdog
Hey look, it's something else that's been implemented in almost every other
JVM language that hasn't made it into Java yet.

~~~
pdpi
Being conservative about what you add to the language is a feature, not a bug.
Not all languages need to be on the cutting edge (even if I personally prefer
the languages that are)

~~~
dloose
See Brian Goetz's talk on language stewardship
([https://www.youtube.com/watch?v=2y5Pv4yN0b0](https://www.youtube.com/watch?v=2y5Pv4yN0b0)).
I'm sure nobody wants to add new and interesting features to Java more than
the people who work on the language every damn day, but they've made a strong
commitment to the community to not break existing code.

~~~
ygra
C# shows pretty compellingly that adding features does not have to break
existing code. For example, you can use every keyword added after 1.0 as
identifier, too.

~~~
SideburnsOfDoom
But it can make things more complex too. e.g. I recently learned that the main
reason that asynchronous C# methods have to be declared "async" is so that the
compiler knows that in the method body, "await" is a keyword. Without this
backward compatibility requirement, it could be inferred from the return type
+ the presence of 1 or more "await" keywords.

------
mythz
Cool, this will help Java from looking so bad:
[https://gist.github.com/mythz/7e263820332a0fcea766](https://gist.github.com/mythz/7e263820332a0fcea766)

From Java LINQ Examples: [https://github.com/mythz/java-linq-
examples](https://github.com/mythz/java-linq-examples)

~~~
kuschku
That code is a wrong equivalence — Java's lambdas make that code already a lot
more readable, but somehow weren't used in that example.

