Numeric
Brian Goetz
brian.goetz at oracle.com
Fri Sep 14 09:27:00 PDT 2012
> Long-term, we need primitive specialization of generics.
Yes. If we start on this now (which we have), the earliest it could
realistically be here is Java 10.
> But shorter
> term, there's an intermediate solution that reduces both boxing and
> combinatorial explosions of special forms. The tradeoff is to accept
> some virtualness, plus a bit of tedium on the part of components
> implementing it:
>
> /**
> * Interface defining access methods for classes that provide numeric
> * results. A {@code Numeric} is not itself an instance of {@link
> * java.lang.Number}, but provides numeric methods to access its
> * primary result or property; normally via the method corresponding
> * to the listed {@code PreferredType} parameter. However, any
> * implementation of this interface must define the non-preferred
> * methods as well (typically by casting the results of the
> * preferred form).
> */
> public interface Numeric<PreferredType extends Number> {
> long getLong();
> int getInt();
> short getShort();
> byte getByte();
> double getDouble();
> float getFloat();
> }
Along with, probably:
PreferredType getBoxed();
> This is a slightly odd interface because the PreferredType parameter
> exists just to communicate the preferred extraction method to
> client programmers.
Not entirely. In some of the streams classes, for example, we do this:
intf MapIterator<K,V> extends Iterator<Mapping<K,V>>
so that we can return a MapIterator as a covariant override of something
that returns Iterator. But, once you know you have a MapIterator, you
can avoid calling the next() method which gets you a boxed Mapping, and
instead call nextKey and friends which are unboxed. But "dumb" code
that wants to treat it as Iterator<T> will be able to do so.
Having the getBoxed() method lets you do the same thing here. The
benefit is mostly for the client, but it also lets you wedge these
things through some existing return paths.
> Looking ahead though, it might also serve as a
> heuristic guide for future efforts on automated generics specialization.
>
> The required tedium is that any class implementing Numeric
> will need to boringly implement five of the methods in terms
> of the one version corresponding to the preferred type. For
> example, class MyFutureDouble would need:
> public double getDouble() { return computeResult(); }
> public long getLong() { return (long)getDouble(); }
> public int getInt() { return (int)getDouble(); }
> public float getFloat() { return (float)getDouble(); }
> public short getShort() { return (short)getDouble(); }
> public byte getByte() { return (byte)getDouble(); }
>
> It would be possible but probably not worthwhile to create
> six subinterfaces that provide defaults of these forms.
Why not worthwhile? We've already stumbled over this pattern a few
times, where you have N methods and typically N-1 are implemented in
terms of one, so you create subinterfaces with defaults and then you've
got a SAM type:
NumericViaDouble n = () -> 3.14d;
> Adopting this has a surprisingly widespread impact on the form of some
> lambdaized/parallelized APIs (mainly internal ones), so it would be a
> good idea to decide on it soon. For example. I could use this now
> in methods like CHM.Parallel().reduceValuesToLong.
Could you characterize the impact here? I think this would be really
useful to understand.
> To be maximally useful, this should probably go into java.lang,
> along side Number. (Otherwise, I'd just stick it in java.util.concurrent
> for our needs and be done with it.)
Yeah, that worked out great with TimeUnit :(
> It would be even nicer if there were a way to do something similar for
> arguments to function-like classes in addition to their results, but
> no solution along these lines applies.
See the approach we've taken for Sink. It's ugly.
More information about the lambda-libs-spec-observers
mailing list