Jacob Schoen
Jacob Schoen

Reputation: 14202

How to handle numbers in a generic fashion?

My question is eerily similar to "Writing a generic class to handle built-in types" including being inspired by the fact of working on a class to handle operations on matrices. Although that question was asked using C# and pointed to an article on Generic Operators.

I don't get it. Java Number does not have an add method so you can have a method like:

public Number myAdd(Number a, Number b){
     return a.add(b);
}

So how do you handle a case where you want to be able to handle multiple types of Numbers in Java?

Upvotes: 5

Views: 4256

Answers (7)

Winston Gutkowski
Winston Gutkowski

Reputation: 1

Actually, I've been working on a generic "real" number class (called 'Value') for a while now, but more as a design exercise than anything; and I can see why it hasn't been done sooner.

First off, you have to have to have some basic rules to work by - I chose to use Java FP (IEEE-754) rules - which means you have have to allow for results like 'infinity' and 'NaN', even if the type doesn't actually support them; and things like reciprocals have proved surprisingly tricky. But I'm getting there, and it's been a fun journey.

One thing that helped me was deciding early on that I needed to deal with "identity" values - specifically 0, 1, and -1, along with -0, +/-infinity and NaN as special cases; the reason being that (for example) multiplication by any of them usually doesn't need any calculation at all. x * 1 = x, x * NaN = NaN, x * 0 = 0 (or NaN), and x * +/-infinity = +/-infinity; and there are similar rules for division, addition and subtraction, which means you can eliminate a lot of dross quickly and consistently. And that leaves implementers to only have to deal with cases that do need calculation.

Of course, not all types will support all identities, but if you make them methods, you can just throw an exception when either an operand or a result is "unsupported".

Hope it helps anyone else interested in giving it a bash, but it's not as simple it looks. :-)

Upvotes: 0

Martin
Martin

Reputation: 2552

One way of implementing a generic add method is to let the left hand argument infer the return type.

package mixins;

import java.math.BigDecimal;

public class Numbers {

    public static boolean isZ(Number n) {
        return n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte;
    }

    public static boolean isR(Number n) {
        return n instanceof Double || n instanceof Float;
    }

    public static BigDecimal add(BigDecimal a, Number b) {
        if (b instanceof BigDecimal) {
            return a.add((BigDecimal) b);
        } else if (isZ(b)) {
            return a.add(new BigDecimal(b.longValue()));
        } else if (isR(b)) {
            return a.add(new BigDecimal(b.doubleValue()));
        }
        throw new IllegalArgumentException("No valid big decimal translation for " + b.getClass());
    }

    public static Integer add(Integer a, Number b) {
        return a + b.intValue();
    }

    public static Long add(Long a, Number b) {
        return a + b.longValue();
    }

    public static Float add(Float a, Number b) {
        return a + b.floatValue();
    }

    public static Double add(Double a, Number b) {
        return a + b.doubleValue();
    }
}

If this is implemented as static methods, you can use static imports.

import static mixins.Numbers.*;

public class Example {

    public static void main(String[] args) {
        BigDecimal fortytwo = new BigDecimal(42);
        BigDecimal fiftyfive = add(fortytwo, 13);
        System.out.println(fiftyfive);
    }

}

Upvotes: 1

Stephen C
Stephen C

Reputation: 718758

I don't get it. Java Number does not have an add method ...

Suppose that java.lang.Number did have an add method or methods, how would you define its signature? How would you define its semantics? How would you deal with "mixed mode" arithmetic?

While it is no doubt possible to answer these questions and design an API, the result is likely to be tricky to use correctly. In addition, it is most unusual for an application to need to perform "representation agnostic" arithmetic. Usually you want / need explicit control over the way that arithmetic is performed and conversions happen. (The Java primitive type promotion rules are already difficult enough for people to get their heads around!!)

All in all, I think that Sun have done us a good service by not trying to support arithmetic in the Number API.

Upvotes: 3

Sarmun
Sarmun

Reputation: 2408

You cannot add any two numbers, for the reasons other pointed out, but you can add numbers of same type, and the result will also be that same type. You can create generic arithmetics in Java, with something like this:

interface Arithmetics<T> {
    T add(T val1, T val2);
}

class IntegerArithmetics implements Arithmetics<Integer> {
    Integer add(Integer val1, Integer val2) { return val1 + val2; }
}

//similarly DoubleArithmetics, BigIntegerArithmetics, ...

Generic Java Math library does exactly that for you.

Upvotes: 0

Nemi
Nemi

Reputation: 3061

Personally, I use BigDecimals for almost everything (but that is mainly because I work with currency values). They handle all numeric values of any size. Because of that, in my opinion they are a generic value and could be used as such in your hypothetical example instead of the Number abstract class. Everything can be turned into a BigDecimal, why not use it?

public BigDecimal myAdd(BigDecimal a, BigDecimal b) {
    return a.add(b);
}

EDIT: To address BigBrothers comment below, you could always use the doubleValue() method to create your own generic method. The only problem with this is that you may lose precision in some rare cases where someone IS passing in a BigDecimal and it is larger than a Double.maxValue

public Number myAdd(Number a, Number b) {
    return new BigDecimal(a.doubleValue() + b.doubleValue());
}

A BigDecimal is a Number, so returning one is of no consequence.

Upvotes: -1

David Moles
David Moles

Reputation: 51093

How good do you want the result to be? If the answer is "good enough, mostly", then this should be sufficent:

public Number myAdd(Number a, Number b){
     return a.doubleValue() + b.doubleValue();
}

But if you want something that, say, matches the promotion semantics of Java primitives, you're probably going to have to write it yourself. And then you'll have to figure out what the rules are for all combinations of "non-standard" Number implementations, including BigDecimal, BigInteger, AtomicDouble, AtomicLong, everything in org.apache.commons.lang.mutable, and any random implementation that somebody might decide to write next Tuesday.

It's not clear what the right thing to do is in most of these cases -- converting everything to BigDecimal, for instance, is not an option if one of the arguments is Apache Commons' Fraction.ONE_THIRD; and besides, doing the conversion in a general way presents the same problems as doing the addition in a general way. But having an add() method on Number would require every Number implementation to handle all these cases -- and that's probably why it isn't there.

Upvotes: 3

user166390
user166390

Reputation:

The fundamental problem is with the Java type system which is very primitive.

Since there is no notion of a sealed set of types in Java (nor is it possible for Java to infer the types like Haskell does) there is no way to make make a general Number + Number -> Number without trickery.

For primitives (and those objects like Integer which can be automagically mapped to them) types promotion and the + operation is part of the language. (And this is actual part of the problem: what should Number a + Number b return where a and b are of different types?)

If you really want this behavior you'll have to find (or create) your own custom class that either uses reflection or a series (of checks and) casts and such. Even if you use generics (remember that generics are type-erased) casting will need to be done.

I imagine these problems are part of the reason why Number is as bland as it is.

Upvotes: 5

Related Questions