These tricks hinge on the fact that compiler and JVM know a richer type system than can be expressed with Java’s syntax. With
var, though, a type does not need to be written out and can instead be determined by the more powerful compiler. This is particularly helpful when working with anonymous classes, which can not be expressed in source code.

Ad-hoc Fields

Every time you call a constructor, you have the chance to add some fields and methods right then and there:

1

2

3

4

5

6

7

8

9

Megacorp megacorp=// ...

Map<Megacorp,Address>headquarters=// ...

ObjectcorpWithHq=newObject(){

Megacorp _corp=megacorp;

Optional<Address>_hq=

Optional.ofNullable(headquarters.get(megacorp));

};

// does not compile

System.out.println(corpWithHq._corp);

The compiler will create an anonymous subclass (in this case of
Object) and then instantiate it. But since the subclass is only created during compilation there is no way to reference it in the source code and so
corpWithHq is declared as an
Object. The unfortunate consequence is that the fields
_corp and
_hq can’t be referenced because they aren’t part of
Object’s API.

If you use
var, on the other hand, things work just fine:

1

2

3

4

5

6

7

8

9

Megacorp megacorp=// ...

Map<Megacorp,Address>headquarters=// ...

varcorpWithHq=newObject(){

Megacorp _corp=megacorp;

Optional<Address>_hq=

Optional.ofNullable(headquarters.get(megacorp));

};

// compiles

System.out.println(corpWithHq._corp);

With var, a variable can be of an anonymous type

Now,
corpWithHq’s type is the anonymous subclass and you can happily toil away with the fields you added.

Enriching Streams

That specific example may not have been overly exciting but there are cases where this approach starts to look very appealing. I’m sure you’ve occasionally written a stream pipeline where you enriched the stream’s elements with some other piece of information, but needed to keep both kinds of elements around. They form a pair, but Java has no tuples, so you start looking for another solution. Maybe this?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

List<Megacorp>megacorps=// ...

Map<Megacorp,Address>headquarters=// ...

Optional<Megacorp>firstWithValidHq=megacorps.stream()

// we stream megacorps, but need to add addresses ...

.map(megacorp->newObject(){

Megacorp _corp=megacorp;

Optional<Address>_hq=

Optional.ofNullable(headquarters.get(megacorp));

})

// ... only for evaluation, though ...

.filter(o->o._hq.isPresent())

.filter(o->isValid(o._hq.get()))

// ... in the end we can get rid of them again

.map(o->o._corp)

.findAny();

Interestingly enough, this example works without
var and already compiles on Java 8 because the streams intermediate’s type,
Stream<$Anonymous>, never needs to be expressed in source ode. With
var, you’re able to declare intermediate variables, though, which wouldn’t work without it:

1

2

3

4

5

6

7

8

9

10

11

12

13

List<Megacorp>megacorps=// ...

Map<Megacorp,Address>headquarters=// ...

// Optional<$Anonymous>

varfirstWithValidHq=megacorps.stream()

.map(megacorp->newObject(){

Megacorp _corp=megacorp;

Optional<Address>_hq=

Optional.ofNullable(headquarters.get(megacorp));

})

.filter(o->o._hq.isPresent())

.filter(o->isValid(o._hq.get()))

// note that the map is gone!

.findAny();

Without the second
map,
firstWithValidHq is an
Optional containing the anonymous class with the two fields
_corp and
_hq_.

Evaluation

As I mentioned in the intro, I don’t think this is a trick you should use frequently, if at all. First, creating the anonymous class is pretty verbose and will often span several lines. Then it mixes two non-trivial features, anonymous classes and type inference, which makes the code harder to read and understand.

What bugs me the most, though, is that it falls apart under simple refactoring. Assume the example we’ve seen grows a bit and somebody wants to extract two methods, one that determines the megacorp with its headquarter and another that processes it:

1

2

3

4

5

6

List<Megacorp>megacorps=// ...

Map<Megacorp,Address>headquarters=// ...

// `determineCorp` must be declared to return a concrete

// type, so no amount of `var` magic is gonna help here

varcorp=determineCorp(megacorps,headquarters);

processCorp(corp);

Using var with anonymous classes makes code harder to read, understand, and refactor

So while refactoring is the right idea, thanks to the ad-hoc fields it’s an order of magnitude more work because a type with the right fields needs to be created and used. That may very well deter developers from actually executing the refactoring and resistance to continuous improvement is not exactly a hallmark of maintainable code.

If you’re looking for alternatives, I sometimes use
Map.Entry for pairs, which Java 9 made much more usable with the static method
Map::entry. Beyond that you could be looking for a library that comes with tuples, something you usually find in functional libraries like Vavr. If you’re patient, you can wait for Project Amber’s data classes, which will make local classes a one-liner.

And just like with fields, if you declare
corp as
Megacorp, the compiler will not let you use the new methods
isSuccessful and
isEvil. With
var it does:

1

2

3

4

5

6

7

varcorp=// like before

System.out.printf(

"Corporation %s is %s and %s.\n",

corp.name(),

corp.isSuccessful()?"successful":"a failure",

corp.isEvil()?"evil":"a failure"

);

It’s the same principle as with ad-hoc fields and I have the same criticism.

Evaluation

Like with ad-hoc fields, the code’s readability and refactorability (what a word) suffer without appreciable benefits.

Alternatively, methods like
isSuccessful and
isEvil could either be members of
Megacorp or a subclass or, if that’s not possible or desirable for whatever reason, they can always be implemented as utility methods. While that makes calling them a little less natural (
Megacorps.isEvil(corp) instead of
corp.isEvil()), it has the added benefit to enable reuse.

Reflection

Using local-variable type inference with
var, it is easy to add fields or methods to objects in an ad-hoc manner, simply by creating an instance of an anonymous class and assigning it to a local variable whose type is inferred. While that is a neat trick, it does not carry its own weight: