PROPOSAL: 'final' without explicit type
Reinier Zwitserloot
reinier at zwitserloot.com
Tue Mar 31 05:52:24 PDT 2009
Yes, the type of the RHS of:
final list = foo ? new LinkedList() : new ArrayList();
is indeed AbstractList<RAW> & Cloneable & Serializable.
The question is: Is that what you make the type of 'list'? That would
be a novel idea: Now we have type variables that can have intersection
types, not just expressions. There's nothing inherent in java stopping
you from doing this for local method variables as you said, but one
should consider the future impact of this: There is absolutely no way
to specify intersection types in either java or in any sort of
reflection tool. Any attempt to add introspection of any sort to types
of method locals is thus going to be a major issue in the future if we
allow intersection types for them. I don't think this proposal
suggests you're allowed to infer the type on fields, regardless of the
issue of intersection types, but what if that seems useful later on?
Then either we overhaul java.lang.reflect, -or-, we all of a sudden
exclude it for fields but allow it (to be backwards compatible) for
method locals. Ugh. Feels hacky.
I would -strongly- suggest at least for project coin to just not allow
intersection types at all, period. A future version of java can add
this support later if it is deemed important. This is analogous to how
java gained intersection types in the fist place, which wasn't in the
earlier versions of java. Until intersection types, the following
expression wasn't even legal:
foo ? new LinkedList() : new Arraylist();
because the type of the expression (and thence also must be the type
of the third arugment to the ?: operator) is decided by the type of
the second argument (in the above example, 'LinkedList', and ArrayList
is not a LinkedList.
this was added without any backwards compatibility issues. The same
can be done for making method local variables support intersection
types.
Note that according to Neal, the complete lub of
Collections.emptyList() and new ArrayList() is JUST 'List<RAW>' and
nothing else, because one of the intersections is a strict subtype of
the other. This is nice, because that would mean that even if we don't
allow intersection types for method-locals, the following:
String foo = someCall();
final list = foo == null ? Collections.emptyList() : Arrays.asList(foo);
would be legal, and would make 'list' be type "List<String>".
A casual glance at large swathes of code finds almost no instances
where I want to shorten my final method-local declaration by excluding
the LHS type, but where I would not be able to because the expression
on the RHS has an intersection type. The slight penalty of being long-
winded in those situations is not as big as the potential for future
pain, IMO.
Marek: I got it right. expressions in java routinely have intersection
types. Just try to ascertain what the type is of:
'foo ? new LinkedList() : new ArrayList()'. It's Serializable &
Cloneable & AbstractList<RAW>. Expressions have had such types since
java 1.4 (1.5? - whichever one relaxed the ternary operator typing).
The entire point of discussion here is: Do we extend this intersection
concept to the type of *variables*? Currently only expressions and the
bounds on generics parameters can be intersections.
Remi: Which problems do you see? Muchos gracias for a link to the
proposal.
--Reinier Zwitserloot
On Mar 29, 2009, at 14:23, Florian Weimer wrote:
> * Reinier Zwitserloot:
>
>> final list = foo ? new LinkedList() : new ArrayList();
>>
>> the type of 'list' is what, exactly?
>
> It's the type specified in section 15.25 of the JLS. I can't find a
> definition of lub(T1, T2) in the spec, but "lub" probably stands for
> "least upper bound", and lub(LinkedList, ArrayList) would be
> AbstractList & Serializable & Cloneable (if I got the types right).
>
>> Serializable? Cloneable? List? They're all valid, so that wouldn't
>> work.
>
> Intersection types are already part of the language, so I don't see
> any problem. The following compiles:
>
> interface I1 {}
> interface I2 {}
>
> static class C1 implements I1, I2 {}
> static class C2 implements I1, I2 {}
>
> static <T extends I1 & I2> void foo1(T foo) {
> }
>
> static void foo1(boolean foo) {
> foo1(foo ? new C1() : new C2());
> }
>
> Existence of intersection types also leaks to the surface during
> overload resolution.
>
> It's just that you can't write down the type of some expressions using
> Java type notation. For local variables, this isn't a problem; only
> debugging information needs to be updated slightly. The effect for
> fields would be more pronounced, and I guess to stay within COIN's
> scope, a proposal would have to exclude inference for fields.
More information about the coin-dev
mailing list