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