Possible regression bug in the compiler.
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Jan 13 14:28:40 UTC 2016
Hi,
I've take a deep look at this; I'm inclined to believe that it was an
issue in JDK 7 that was later addressed in 8 and 9.
The way diamond works (as specified in the JLS), is that for each
constructor, we derive a generic method - in the case of your Model
class, the following methods will be derived:
<Z> m(Z z) //this corresponds to Model(T v)
<Y> m(Value v) //this corresponds to Model(Value)
Now, both methods are clearly applicable with a ValueHolder argument. So
the question is - which is most specific?
Following the process in the JLS, the most specific method should be
m(Value) - because:
* you can call <Z>m(Z) with a Value argument
* you cannot call <Y>m(Value) with a Z argument [where Z is _not_
treated as an inference variable]
So, m(Value) is most specific, and therefore, its associated constructor
- Model(Value) is the one selected for the instance creation expression.
That said, I understand where you are coming from, and I do find this a
bit surprising - after all the inferred type for the instance creation
expression is Model<ValueHolder>, and the type Model<ValueHolder> has
two constructors:
Model(ValueHolder)
Model(Value)
of which the former seems clearly the most specific (and is in fact
selected in the absence of diamond).
So, the key difference with diamond, is that the most specific selection
occurs _ahead_ of inference variable instantiation - therefore the
compiler doesn't have enough info to realize that Model(T) is truly most
specific.
This behavior can be also reproduced without diamond:
interface Value {
}
class ValueHolder implements Value {
}
class Model<T> {
static <Z> Model<Z> make(Z z) { System.err.println("Class"); return
null; }
static <Z> Model<Z> make(Value v) { System.err.println("Interface");
return null; }
public static void main(String... argv) {
ValueHolder holder = new ValueHolder();
Model<ValueHolder> m0 = Model.make(holder);
}
}
Which prints 'Interface'.
That said, I will leave this with our spec experts - to determine as to
whether something has to be tweaked here.
Thanks
Maurizio
On 12/01/16 21:11, Andrej Golovnin wrote:
> Hi all,
>
> consider following example:
>
> interface Value {
> }
>
> class ValueHolder implements Value {
> }
>
> class Model<T> {
>
> Model(T v) {
> System.out.println("Called constructor with generics");
> }
>
> Model(Value v) {
> System.out.println("Called constructor with interface");
> }
>
> public static void main(String... argv) {
> ValueHolder holder = new ValueHolder();
> Model<ValueHolder> m0 = new Model<>(holder);
> Model<ValueHolder> m1 = new Model<ValueHolder>(holder);
> }
>
> }
>
> When I compile and execute it with Java 7, then it outputs:
>
> Called constructor with generics
> Called constructor with generics
>
> When I compile and execute it with Java 8 and Java 9, then I get this output:
>
> Called constructor with interface
> Called constructor with generics
>
> In Java 7 the compiler chooses in both cases, with the diamond
> operand and with the provided type parameter, the constructor with generics.
> In Java 8 and 9 only when I provide the type parameter.
>
> Is it a bug or desired change?
> From my standpoint of view there should be no difference,
> if I use the diamond operand or not.
>
> Best regards,
> Andrej Golovnin
More information about the compiler-dev
mailing list