5

This is tangentially related to Why doesn't Java 8's ToIntFunction extend Function<T, Integer>

I find it mildly annoying that the primitive versions of Optional and various functional interfaces use different method names, like getAsInt() instead of get() and applyAsDouble(x, y) instead of apply(x, y). I wondered why they didn't just go with the same method names. After all, all of the streams use map, filter, reduce, etc, regardless of type, which seems to make perfect sense. There doesn't seem to be a point in having doubles.filterDoubles(DoublePredicate) and ints.reduceAsInt(IntBinaryOperator). But then why would we have to do ints.reduce(Integer::sum).getAsInt()?

In particular, it's interesting to note the asymmetry. Both ints.reduce(...).get() and ints.reducePrimitive(...).getAsPrimitive() would be consistent. Yet we have neither.

One reason I could think of was wanting to make inheritance possible. If we wanted an OptionalInt to also be an Optional<Integer>, then we couldn't have both an int get() and an Integer get() method. So then it would make sense to extend Optional<Integer>, inherit the autoboxing method, and then also add an additional primitive-only method for avoiding the autoboxing for performance. Same idea goes for functions. But as the question above discusses, the primitive functional interfaces do not extend the generic ones, and neither do the primitive Optional classes.

Is there something I'm not seeing? Would using get and apply break something? Or is this just an accidental inconsistency in the API, analogous to String.length() vs List.size()? I suppose it's possible that the designers wanted to leave the door open to using inheritance as described above in the future. Can anyone think of another reason?

chrfin
  • 76
  • 4
  • If one were to demand consistency even in `OptionalPrimitive`s themselves, it'd be expected to also have `ifPresentAsInt`, `orElseAsInt`, etc. :-) - it's more likely that this is just accidental inconsistency. I can't imagine that `OptionalPrimitive`s were meant to interoperate with `Optional`s, the classes are already final; but I might be myopic. – ernest_k Nov 03 '20 at 05:30
  • Hehe good point @ernest_k. :D Yeah, I've noticed they are final. I *highly* doubt they would ever make them extendable, but it's at least a possibility. However, the functional interfaces are (obviously) not final. So it's more plausible to imagine a future proofing motivation in that case. It would also work much better practically, since an `OptionalInt extends Optional` would have a redundant autoboxed Integer in the super class, while `IntFunction extends Function` could have a `public default R apply(Integer i) { return applyAsInt(i); }` override. – chrfin Nov 03 '20 at 05:42
  • 1
    Beside the technical limitations preventing `int` and `Integer` to be mixed in the same type parameter `T` (and perhaps the performance point you raised), there's also the question of semantics. I find that they separated primitive-typed optionals because you can't have a null primitive. If you have a null `Integer` an take it to `OptionalInt`, you'll be met with a NullPointerException before the instance is even created. Same for Function types, such as `ToIntFunction`. – ernest_k Nov 03 '20 at 05:55
  • 1
    Your examples `Stream#filter` and `Stream#reduce` _do_ have primitive versions though, .e.g `IntStream#filter` and `IntStream#reduce`. Furthermore, `Stream#map` has the whole `#mapToInt`, `#mapToDouble`, and the converse `IntStream#mapToObj`. The main point is that primitives (`int`, `long`, `double`, etc) are not objects, and thus cannot be `null`. Similarly, they avoid a lot of overhead of objects, while being a little more obtuse to use in a generic sense. The reason _why_ these plethora of interfaces/types exist is because of the lack of primitive generics – Rogue Nov 03 '20 at 05:57
  • @Rogue yes, that is exactly my point. `IntStream#filter` is called `IntStream#filter`. It is not called `IntStream#filterAsInt`. The overhead of objects is the reason why primitive versions exist. That does not explain why one would want to give those versions special method names. – chrfin Nov 03 '20 at 06:14
  • @ernest_k Oh, that's actually a *really* good point! `getAsInt` could be a good way of saying `getSomethingThatIsGuaranteedNotToBeNull`. Edit: Hmm, though the whole point of `Optional` is that it can never give you a null value. – chrfin Nov 03 '20 at 06:15
  • @chrfin the overhead for primitives makes sense, but not for `Optional`. After all it holds a single value, so the overhead is totally neglectable. _Still_ they added `OptionalXXX`, oh well... I agree with Ernest - we will most probably never know why these inconsistencies. – Eugene Nov 22 '20 at 17:56

0 Answers0