class A {}
class B extends A {}
class Holder<T> {
T object;
Holder(T object) {
this.object = object;
}
}
There's a Holder class to hold some object, created using generics. In the main(), when initialized using the diamond operator it doesn't compile (Java 7) with derived class passed to the Holder's constructor (required A / found B):
public static void main(String[] args) {
Holder<A> holder = new Holder<>(new B());
}
But it compiles and works if the base type is specified in the right part:
public static void main(String[] args) {
Holder<A> holder = new Holder<A>(new B());
}
Why? Doesn't diamond operator define the right part of the assignment with the same type parameters as the left side?
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());
Holder<B>
and gives a compile error because a Holder<B>
can't be converted into a Holder<A>
.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.