Possible regression bug in the compiler.
Andrej Golovnin
andrej.golovnin at gmail.com
Wed Jan 13 21:17:05 UTC 2016
Hi Maurizio,
thank you very much for taking time to look at it
and for the detailed explanation!
The most frustrating part of this problem is that you notice
it only at runtime because the compiler does not raise a warning or an error.
Anyway we have changed now our code to avoid this problem.
Thanks and best regards,
Andrej Golovnin
> On 13.01.2016, at 15:28, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
>
> 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