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