Static fields in specialized classes
Brian Goetz
brian.goetz at oracle.com
Wed Oct 15 15:25:58 UTC 2014
> I'm afraid I'm not paid to think full time about the issues like you
> are, but where I can and will speak up is where I have concerns about
> the "feel of Java" in future proposals.
You're free to speak up. I would hope that before doing so, you'll
first make a fair attempt to learn what is being proposed, rather than
shooting from the hip based on your first glimpse of what "new generics"
might look like. It takes some time to get used to new ideas.
(Remember, people bitched mightily about how complicated generics were
when they were introduced.)
>> Similarly, Foo<int> can't extend Foo<?>
>
> That is deeply unappealing.
I don't disagree, though I might quibble about the depth of "deeply".
I'll just note that the alternatives in evidence are less appealing.
> just want to accept any Foo. If I write a method
>
> process(Foo<?>)
>
> today in Java 8, I expect it to be able to process a Foo with any
> generic
I'll accept the goal -- it is essential that a method can process any
Foo<T>. Just not the mechanism -- see below.
> (ie. I'm saying that I don't care about the generic parameter,
> and just want to use the other aspects of the class). To argue that in
> the future such a method will not be able to accept a Foo<int> (or any
> other value type) is as I said, deeply unappealing, and in a
> reasonable sense, not backwards compatible.
I would argue that the trick of simulating generic methods with
process(Foo<?>) was *always* broken. (Collections does this for
backward compatibility with pre-generified collections, but this was
always a trick for migration, not for simulating generic methods.) What
you should have done even in Java 5 was
<T> void process(Foo<T>) { ... }
instead. It's just that because of erasure, you could get away without
using the wildcard parameter and nothing bad happens. So yes, you will
no longer be able to get away with that trick for any-generic T, but you
will still be able to use the right tool -- generic methods -- to write
fully generic code. So if you want to write methods that are generic
across all Foo<T> instantiations, just do:
<any T> void process(Foo<T> foo) { ... }
and you're fine.
So if the complaint is "I need to be able to write a method that accepts
any Foo<T>", we're way ahead of you -- use the tool that was always
there for that, it will work just fine. If the complaint is "I want to
continue to use things that only work in an erased world in a
semi-reified world", well, you're going to need to let go of your
crutch. Refactoring
void process(Foo<?>)
into
<any T> void process(Foo<T>)
is not hard. And there's no backward-compatibility issue -- existing
code will continue to work as before.
> Obviously alternative designs are hard, given that you've gone a fair
> way down the current specialism route. I believe that what I really
> want is for values to behave in code as fast objects, not for values
> to behave with all the quirks of primitives.
Yeah, that's a nice idea, and it might be possible to get that at the
boundary (where values are passed from one method to another.) Where
this falls down is in representation of things like arrays; you want
ArrayList<int> to be backed by an int[], not an Integer[]. You may be
able to make boxing faster (and we should!), but when the data hits the
heap, erasure won't cut it.
> (Primitives are bad,
> special cases in most framework code, and this thread appears to be
> expanding the scope of those bad, special cases, not reducing it).
I think you will find you are wrong about that. This is unifying the
treatment of primitives in generics, which is one part of the puzzle;
there are others, but when we're done, we should be finished with
silliness like writing nine versions of methods like Arrays.fill.
Avoiding special-casing of primitives is part of the design goal here,
by unifying all primitives in the greater family of "values".
> Perhaps what Ben, Paul and I are ultimately saying is that the concept
> of the Class mapping to the piece of source code is valuable, and
> shouldn't be lost.
Yes, this is the important part, and it wasn't lost on me. People have
intuitive ideas of what "instanceof Foo" means, and that is important.
> As part of this, I don't consider it desirable to be able to overload like this:
>
> process(Foo<String>)
> process(Foo<int>)
>
> but not:
>
> process(Foo<String>)
> process(Foo<Integer>)
>
> It should be both or neither.
This is a valid opinion -- though not the only valid opinion! This is a
place where we have a choice; keep the existing rules (on the theory
that people have finally figured them out), or extend them based on what
is possible (on the theory that we should make the restrictions as
minimal as they can be.) And either way will upset someone! So this is
one of those stewardship choices the EG will get to eventually make. (A
nearly identical one is "implements Comparable<int>, Comparable<long>".)
More information about the valhalla-dev
mailing list