I don't think it would be wise to tie operator overloading to Number.
Number defines a series of conversions that make some sense for those
types that represent subsets of the real line.
I wasn't thinking beyond the real line to the more abstract stuff
mathematicians futz around with and consider to have addition and
multiplication.
But short of giving general operator overloading to the masses (which
the gurus in charge of Java clearly do not want to do), there *is* a
way: introduce an abstract superclass, say, AbstractNumber or
ArithmeticComposable or something for "things that aren't necessarily
normal numbers, but have the basic arithmetic operations".
I think it might be better to create a marker interface
java.math.Arithmetic.
That sneaks in arbitrary operator overloading through the back door, as
people slap "implements java.math.Arithmetic" on
"JFoobarSQLQueryEnumerator" and implement ".plus" for it.
Even as things go, I'd like there to be some compiler magic making the
ArithmeticComposables (or whatever) have automatically final fields,
anything with that as an ancestor, as well. It should be that a += b
changes what math object the reference a points to, but doesn't change a
for anybody else, so all the arithmetic operations should return new
objects (as currently seems to be the case for BigInteger and
BigDecimal), not mutate the original.
I do like the idea of a fixed mapping from
operators to method names that are normal identifiers.
It's also the only way to do it without adding new keywords and syntax
in a probably source-compatibility-breaking way. And do we really want
C++'s "operator+" notation?
I don't think
all arithmetic types should be required to support all operations. For
example, consider negate and an unsigned type, or reciprocal and an
integer type.
There's two approaches to dealing with that.
1. AbstractNumber implements everything, but they all throw
UnsupportedOperationException. You override what you can support.
2. AbstractNumber doesn't even specify some of these, but +, -, etc.
may expand into them anyway, and the compiler will then complain
if the corresponding named method is not found.
One problem with this, though, is that implementing a - b when a is
primitive and b isn't, and likewise a / b, seems to require having
negate and reciprocal, respectively, supported by whatever the return
type of b - a and b / a would be.
There's also a possible precision problem with a/b being computed as
1/(b/a) in these cases.
As an alternative, perhaps boxing transformations can be expected
instead: valueOf(int), etc. methods will be used so if b is of an
ArithmeticComposable type Foo with a valueOf(int) method and a is an
int, or can be widened to int (short, byte), then a + b becomes
Foo.valueOf(a).plus(b).
Of course, valueOf may need to cope with values out of range. What if
Foo is your hypothetical unsigned type and a is -42? RuntimeExceptions
seem indicated in these cases. We already have an ArithmeticException
class, currently used for BigInteger/BigDecimal ops that result in
division by zero or non-representable values (in the case of a BD with
unlimited precision -- another spot where we could sorely use a Rational
class). I'd suggest including an ArithmeticRangeException subclass of
that for use in such valueOf methods.
And once boxing conversions are being included, why not go ahead and
allow any ArithmeticComposable with intValue or similarly-named methods
be autounboxed when used in contexts that expect a primitive? (Though a
+ b and other arithmetic situations will never unbox b instead of boxing
a, presuming that the specialized type of b is desired. Manual unboxing
can be done if the opposite is desired, e.g. a + b.intValue().)