52

I have two Numbers. Eg:

Number a = 2;
Number b = 3;
//Following is an error:
Number c = a + b;

Why arithmetic operations are not supported on Numbers? Anyway how would I add these two numbers in java? (Of course I'm getting them from somewhere and I don't know if they are Integer or float etc).

MPelletier
  • 16,256
  • 15
  • 86
  • 137
amit
  • 10,612
  • 11
  • 61
  • 60
  • 1
    Are they actually assigned like that? I mean, is Number a = primitiveNumber valid? If you are getting them from some Method which returns "something extends Number" you could actually check whether the returned Number is an instanceof Double or Float or BigDecimal or whatever. – Tedil Apr 27 '10 at 14:20
  • 1
    @Tedil Yes an assignment of this kind is possible since Java 1.5, thanks to a feature know as "autoboxing". The primitive number `2` is an `int`, which is boxed to an `Integer`, which is a subclass of `Number`. – Christian Semrau Apr 27 '10 at 14:23

10 Answers10

26

You say you don't know if your numbers are integer or float... when you use the Number class, the compiler also doesn't know if your numbers are integers, floats or some other thing. As a result, the basic math operators like + and - don't work; the computer wouldn't know how to handle the values.

START EDIT

Based on the discussion, I thought an example might help. Computers store floating point numbers as two parts, a coefficient and an exponent. So, in a theoretical system, 001110 might be broken up as 0011 10, or 32 = 9. But positive integers store numbers as binary, so 001110 could also mean 2 + 4 + 8 = 14. When you use the class Number, you're telling the computer you don't know if the number is a float or an int or what, so it knows it has 001110 but it doesn't know if that means 9 or 14 or some other value.

END EDIT

What you can do is make a little assumption and convert to one of the types to do the math. So you could have

Number c = a.intValue() + b.intValue();

which you might as well turn into

Integer c = a.intValue() + b.intValue();

if you're willing to suffer some rounding error, or

Float c = a.floatValue() + b.floatValue();

if you suspect that you're not dealing with integers and are okay with possible minor precision issues. Or, if you'd rather take a small performance blow instead of that error,

BigDecimal c = new BigDecimal(a.floatValue()).add(new BigDecimal(b.floatValue()));
Pops
  • 30,199
  • 37
  • 136
  • 151
  • 4
    You're almost right. But I don't agree with "... so it knows it has 001110 but it doesn't know if that means 9 or 14 or some other value.". Java HAS runtime type information. Java WILL know whether the runtime type of a Number object is Integer or Double or something else. Theoretically, you could begin distinguishing the types and begin programming public Number plus(Number a, Number b) { if (a instanceof Integer && b instanceof Integer) { return new Integer(a.intValue + b.intValue(); } ... and so on... – fjf2002 Nov 19 '14 at 16:48
  • 1
    Why will someone parse the number to a integer instead of just using the integer type then? – White_King Jan 28 '19 at 01:40
24

It would also work to make a method to handle the adding for you. Now I do not know the performance impact this will cause but I assume it will be less than using BigDecimal.

public static Number addNumbers(Number a, Number b) {
    if(a instanceof Double || b instanceof Double) {
        return a.doubleValue() + b.doubleValue();
    } else if(a instanceof Float || b instanceof Float) {
        return a.floatValue() + b.floatValue();
    } else if(a instanceof Long || b instanceof Long) {
        return a.longValue() + b.longValue();
    } else {
        return a.intValue() + b.intValue();
    }
}
SkidRunner
  • 813
  • 8
  • 12
  • 3
    You could save repeating yourself by using a ``, so you only have to check the type of `N a`. – ndm13 Feb 09 '16 at 12:44
  • @ndm13 in the above snippet I am only checking the type to tell witch method to use. – SkidRunner Feb 11 '16 at 17:32
  • I meant instead of having to check the types of both a and b. The generic would ensure they are the same type, so you would only need to check that. – ndm13 Feb 11 '16 at 18:49
  • 2
    So I will agree that using generics is a better method. But you would still need to check the type of both a and b. `Number number = addNumbers(new Double(1.275), new Long(14));` only checking a returns correct Double. `Number number = addNumbers(new Long(14), new Double(1.275));` only checking a returns incorrect Long. – SkidRunner Feb 12 '16 at 18:12
  • `public static N addNumbers(N a, N b)` would ensure the same typing, which would only require checking one. Your method would require checking both and allow for unlike numbers to be added. We were thinking along different lines; sorry to confuse us both! – ndm13 Feb 12 '16 at 18:17
  • One more comment and I will be done. you are assuming that they will specify `Double number = addNumbers...` forcing use of two Doubles for addition but if written like I had above `Number number = addNumbers...` you can now pass any two objects that extend the number class. so long story short even in your method you should still check the type of both a and b. – SkidRunner Feb 12 '16 at 18:22
  • It doesn't have to be a Double. It could be an Integer, Float, or any other generic wrapper. Although I'd probably want to put an exception condition on the end to handle things like AtomicInteger and other "Numbers". Regardless, Double + Double = Double, Float + Float = Float, etc. Your class still has flexibility in being able to return the sum of two unlike numbers, though. – ndm13 Feb 12 '16 at 18:26
  • @ndm13 your method _also_ permits passing in two unlike numbers, since "Number" is a valid parameterization of "N extends Number". – Alice Purcell Jan 23 '19 at 12:04
  • @AlicePurcell ...which would give the same error that this code would, with fewer lines and more type safety. You'd also have to explicitly cast your numbers to `Number` to encounter this error. I don't see your point. – ndm13 Jan 23 '19 at 12:18
  • @ndm13 no explicit casting necessary, addNumbers((Long) 1L, (Double) 1.0) for instance compiles just fine: Java just picks Number for N. Also, if you try changing the given answer to have your generic signature, you'll find it no longer compiles, since you "cannot convert from double to N"; you'll need to add two casts to every return statement. I suggest you actually try playing with this in a real compiler. – Alice Purcell Jan 25 '19 at 21:05
  • @AlicePurcell The design isn't perfect, but neither is the original. You'd have to use `(N)Double.valueOf(a.doubleValue() + b.doubleValue());`, for instance. The `Number` class is poorly designed in my opinion: the process would be easier if precisions were nested. To each their own implementation, as long as it's well-documented! – ndm13 Jan 25 '19 at 21:22
  • @ndm13 You are backtracking from your original claim that you "would ensure the same typing" and "only require checking one". At this point, there is no benefit to your interface change, and it simply forces a more complex implementation. It is objectively worse. – Alice Purcell Jan 26 '19 at 14:59
  • @AlicePurcell I may have not been clear earlier. You only need to check one type when calling the method with two of the same type, which yields the additional advantage of returning that type. I already mentioned that the original handles mixed types better, and if you wanted that ability you could check both variables in mine too (but would still have to return Number). If you're casting to a type after you've run the function, then I don't see the point: you'd just sum the `.whateverValue()` calls instead. – ndm13 Jan 26 '19 at 15:18
  • 4
    @ndm13 "You only need to check one type when calling the method with two of the same type" This isn't true, though. That's the whole point. Your signature allows dissimilar types to be passed in in exactly the same way as the original. – Alice Purcell Jan 28 '19 at 13:59
14

The only way to correctly add any two types of java.lang.Number is:

Number a = 2f; // Foat
Number b = 3d; // Double
Number c = new BigDecimal( a.toString() ).add( new BigDecimal( b.toString() ) );

This works even for two arguments with a different number-type. It will (should?) not produce any sideeffects like overflows or loosing precision, as far as the toString() of the number-type does not reduce precision.

Fritz Mock
  • 151
  • 1
  • 3
  • 2
    If only we could have Class> BigDecimal.getPrimitiveType() returning int.class or double.class or whatever – Whimusical Dec 16 '15 at 01:45
9

java.lang.Number is just the superclass of all wrapper classes of primitive types (see java doc). Use the appropriate primitive type (double, int, etc.) for your purpose, or the respective wrapper class (Double, Integer, etc.).

Consider this:

Number a = 1.5; // Actually Java creates a double and boxes it into a Double object
Number b = 1; // Same here for int -> Integer boxed

// What should the result be? If Number would do implicit casts,
// it would behave different from what Java usually does.
Number c = a + b; 

// Now that works, and you know at first glance what that code does.
// Nice explicit casts like you usually use in Java.
// The result is of course again a double that is boxed into a Double object
Number d = a.doubleValue() + (double)b.intValue();
Kevin Ji
  • 10,479
  • 4
  • 40
  • 63
Philip Daubmeier
  • 14,584
  • 5
  • 41
  • 77
  • 3
    I understand this. But strange that it is not directly supported on Number class itself. – amit Apr 27 '10 at 13:11
  • @amit.dev Not all subclasses can implement this in a way you would expect. Especially with the Atomic types it's hard to define a usefull contract for e.g. add. And Short.add(Long) is also bound to give trouble. – extraneon Apr 27 '10 at 13:13
  • 1
    @amit.dev: why should it be directly supported? That makes no sense, because it would demand java to support weird implicit casts or operator overloads for every permutation of types to add. I added an example code to my answer. Maybe that makes it more clear. – Philip Daubmeier Apr 27 '10 at 13:38
  • Note: The wrapper class for `int` is actually `Integer`. – Cam Apr 27 '10 at 13:39
  • 5
    @Philip The `Number a = 1.5;` is a double. `1.5f` is a float. – Christian Semrau Apr 27 '10 at 14:25
3

Use the following:

Number c = a.intValue() + b.intValue(); // Number is an object and not a primitive data type.

Or:

int a = 2;
int b = 3;
int c = 2 + 3;
Elie
  • 6,915
  • 7
  • 31
  • 35
3

I think there are 2 sides to your question.

Why is operator+ not supported on Number?

Because the Java language spec. does not specify this, and there is no operator overloading. There is also not a compile-time natural way to cast the Number to some fundamental type, and there is no natural add to define for some type of operations.

Why are basic arithmic operations not supported on Number?

(Copied from my comment:)

Not all subclasses can implement this in a way you would expect. Especially with the Atomic types it's hard to define a usefull contract for e.g. add.

Also, a method add would be trouble if you try to add a Long to a Short.

extraneon
  • 23,575
  • 2
  • 47
  • 51
1

If you know the Type of one number but not the other it is possible to do something like

public Double add(Double value, Number increment) {
   return value + Double.parseDouble(increment.toString());
}

But it can be messy, so be aware of potential loss of accuracy and NumberFormatExceptions

muttonUp
  • 6,351
  • 2
  • 42
  • 54
0

Number is an abstract class which you cannot make an instance of. Provided you have a correct instance of it, you can get number.longValue() or number.intValue() and add them.

fastcodejava
  • 39,895
  • 28
  • 133
  • 186
0

First of all, you should be aware that Number is an abstract class. What happens here is that when you create your 2 and 3, they are interpreted as primitives and a subtype is created (I think an Integer) in that case. Because an Integer is a subtype of Number, you can assign the newly created Integer into a Number reference.

However, a number is just an abstraction. It could be integer, it could be floating point, etc., so the semantics of math operations would be ambiguous.

Number does not provide the classic map operations for two reasons:

First, member methods in Java cannot be operators. It's not C++. At best, they could provide an add()

Second, figuring out what type of operation to do when you have two inputs (e.g., a division of a float by an int) is quite tricky.

So instead, it is your responsibility to make the conversion back to the specific primitive type you are interested in it and apply the mathematical operators.

Uri
  • 88,451
  • 51
  • 221
  • 321
  • Wrong, you cannot create an instance of an abstract class. – Cam Apr 27 '10 at 13:40
  • 1
    @incrediman: Where did I indicate that I am generating instances of an abstract class? I explicitly indicated that an integer is created and then assigned into the Number reference. – Uri Apr 27 '10 at 13:53
  • @incrediman, actually, it is legal to declare a reference to an abstract class and point it at an existing object; you just can't declare a new instance of an abstract class. – Pops Apr 27 '10 at 13:58
  • @incrediman : Wrong, you cannot say Uri said that when he actually begin with the line: *...be aware that Number is an abstract class...* :) :) :) – OscarRyz Apr 27 '10 at 14:00
0

The best answer would be to make util with double dispatch drilling down to most known types (take a look at Smalltalk addtition implementation)