First observation:
Holder<B> h = new Holder<>(new B());
compiles both with Java 8 and Java 7, and both create a Holder<B>
in that scenario. So using a <>
with a constructor that takes arguments is fine.
However with:
Holder<A> h = new Holder<>(new B());
- Java 7 first evaluates the right hand side, determines that it is a
Holder<B>
and gives a compile error because a Holder<B>
can't be converted into a Holder<A>
.
- Java 8 goes one step further and infers that
new Holder<A>(new B())
could actually work and does that auto-magically. This is thanks to a new feature called "target typing" - see the last section of the tutorial on type inference for an overview.
In more details, the improvement in Java 8 is due to the introduction of poly expressions (emphasis mine):
The type of a standalone expression can be determined entirely from the contents of the expression; in contrast, the type of a poly expression may be influenced by the expression's target type (§5 (Conversions and Contexts)).
This is a very powerful feature of Java 8 (Java 7 only offers standalone expressions that don't take the expression context into account).
A generic class instance creation is a poly expression and JLS #15.9 explains (emphasis mine):
A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3). Otherwise, it is a standalone expression.
Because of that new rule, Java 8 allows you to use the second form above and automatically infers that new B()
should be treated as an A
(widening reference conversion) and that you meant to create a Holder<A>
in that context.