Notes on implementing concise calls to constructors with type parameters

Maurizio Cimadamore Maurizio.Cimadamore at Sun.COM
Thu May 14 02:00:47 PDT 2009


Neal Gafter wrote:
> Maurizio-
>
> When I discussed the proposed implementation (and specification) strategy
> with Joe Darcy, I did NOT propose to actually generate any code for
> synthetic static factories.  Rather, the strategy is for the compiler
> to *imagine
> *the existence of a static method *for the purposes of type inference only*.
>   
Neal
I know that you weren't proposing to generate actual code for static 
factories.
> I agree that your technique has almost the same effect as the one I
> proposed, except that you fail to use the constructor arguments as input to
> type inference.  I consider that a severe disadvantage.  It will result in a
> continued need for static factory methods just to get type inference.
>   
Let me start by saying that all the examples I found in this thread 
exploits inference from the return type. But there's a more compelling 
reason for which I think my solution is pragmatically a good idea: it 
doesn't alter the way in which method (constructor) resolution is 
performed. That is, after the the site has been inferred (from the 
assignment context) standard method resolution can be applied to resolve 
the constructor to be called in a straightforward way. The second 
advantage is that the inferred type is always somehow mandated by the 
declared type which, given the fact that Java is a strongly typed 
language I consider it to be a good side-effect, e.g.:

List<Number> ln = new ArrayList<>(1);

This would be inferred to ArrayList<Number> with my proposal; with your 
proposal the above code will be flagged with an error, as the inferred 
type ArrayList<Integer> (which I agree is more specific) is not 
compatible with the expected type List<Number>. The only alternative 
would be to use a wildcard in the LHS.

In other words I think that my strategy is less powerful (as stated in 
the previous mail - no secret about that :-) ) but that leads to more 
predictable results - e.g. in terms of refactoring existing code, users 
can most of the time just drop the generics in the RHS, replacing them 
with the diamond notation, and the semantics won't change. With your 
approach this simple refactoring could lead to compile-time errors.

Regarding the fact that ForAll is not present in the JLS - I totally 
agree - they are not part of the JLS whatsoever. In fact this is an 
'implementation' strategy. The JLS should find an alternate way to 
specify the behavior of the diamond notation - but it doesn't seem - it 
should simply reformulate 15.12.2.8 so that it can be applied to class 
type variables as well as to method type variables.

Maurizio
> Cheers,
> Neal
>
> On Wed, May 13, 2009 at 9:49 AM, Maurizio Cimadamore <
> Maurizio.Cimadamore at sun.com> wrote:
>
>   
>> Hi
>> I'm working at the implementation of the coin item 'concise calls to
>> constructors with type parameters'[1][4]. I've been following the
>> previous emails about this subject and I noted that the proposed
>> implementation strategy is essentially to mimick the diamond notation by
>> providing synthetic factory methods. As already noted[2] this approach
>> has some complications:
>>
>> * Generic constructors that declare their own type variables
>> * accessibility modifiers
>> * var-args
>> * boxing/unboxing conversion
>>
>> I came out with an alternate implementation[3] strategy which makes use
>> of the javac's ForAll type:
>>
>> *) When a call to a constructor exploiting the diamond operator is found
>> (e.g. new ArrayList<>()) the type of the new expression should be a
>> ForAll - in this particular example, a type F type where the F.tvars = E
>> (type variable declared by ArrayList) and where F.qtype = ArrayList<E>.
>>
>> *) When javac checks the new expression actual type against the expected
>> type E (e.g. List<String>) simply re-use Infer.instantiateExpr(F, E),
>> where F is the ForAll type calculated as above and E is the expected
>> type. Javac will apply 15.12.2.8 in order to infer all the type
>> variables in F exploiting (i) info about the expected type E and (ii)
>> type-variables (non -recursive) declared bounds. In this very simple
>> case javac will infer E to be String and the 'new' expression would
>> type-check without problems.
>>
>> This approach has the advantage of not requiring additional synthetic
>> code/type/symbol to be generated on the fly by javac.
>>
>> The main difference between my implementation strategy and the proposed
>> one (exploiting synthetic factory methods) is that my implementation
>> performs only a single round of inference, in particular the one
>> described in JLS3 15.12.2.8; the implementation strategy requiring
>> synthetic factory methods is slightly more powerful as it can run a full
>> inference round (JLS3 15.12.2.7 followed by 15.12.2.8). In terms of Java
>> code this means that, given the following code:
>>
>> class Foo<X> {
>> ...
>> Foo(X x) { ... }
>> ...
>> }
>>
>> Foo<?> foo = new Foo<>("Hello!");
>>
>> my implementation infers Foo<Object> while an implementation through
>> static factories will be able to infer Foo<String> (because it would
>> take into account inference from actual constructor parameters as well).
>>
>> Maurizio
>>
>> [1]
>> http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000009.html
>> [2] http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000075.html
>> [3] http://cr.openjdk.java.net/~mcimadamore/6840638/webrev.0/<http://cr.openjdk.java.net/%7Emcimadamore/6840638/webrev.0/>
>> [4] http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6840638
>>
>>
>>     
>
>   




More information about the coin-dev mailing list