11

I would like to generically add numbers in java. I'm running into difficulty because the Numbers class doesn't really support what I want to do. What I've tried so far is this:

public class Summer<E extends Number> {


    public E sumValue(List<E> objectsToSum) {
        E total = (E) new Object();
        for (E number : objectsToSum){
            total += number;
        }
        return null;

    }

Obviously this will not work. How can I go about correcting this code so I could be given a list of <int> or <long> or whatever and return the sum?

4
  • 1
    What if you have a list which contains different Number subclasses? E.g. Arrays.asList(5L, 1e100, 42)? Commented Dec 29, 2011 at 15:40
  • Duplicate of stackoverflow.com/questions/285754/… Commented Dec 29, 2011 at 15:45
  • @kan - I would expect I'd get an error since they're not all of type E, right? Commented Dec 29, 2011 at 16:45
  • @DavidM.Coe Suppose that E == Number. So, which error do you expect? Commented Dec 29, 2011 at 16:55

9 Answers 9

18

In order to calculate a sum generically, you need to provide two actions:

  • A way to sum zero items
  • A way to sum two items

In Java, you do it through an interface. Here is a complete example:

import java.util.*;

interface adder<T extends Number> {
    T zero(); // Adding zero items
    T add(T lhs, T rhs); // Adding two items
}

class CalcSum<T extends Number> {
    // This is your method; it takes an adder now
    public T sumValue(List<T> list, adder<T> adder) {
        T total = adder.zero();
        for (T n : list){
            total = adder.add(total, n);
        }
        return total;
    }
}

public class sum {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(4);
        list.add(8);
        CalcSum<Integer> calc = new CalcSum<Integer>();
        // This is how you supply an implementation for integers
        // through an anonymous implementation of an interface:
        Integer total = calc.sumValue(list, new adder<Integer>() {
            public Integer add(Integer a, Integer b) {
                return a+b;
            }
            public Integer zero() {
                return 0;
            }
        });
        System.out.println(total);
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

+1 because its more specific then the accepted answer. However it doesnt really solve the problem at hand, since now the problem is in writing a 'generic' adder.
@Stefan It is as generic an adder as it gets, at least in Java with its type erasure rules. There is no other way to supply the addition primitive in Java other than through an interface of some sort.
You still have to provide an implementation of an adder for every Number type, just like you would have to for his Summer. I would go as far as saying this problem isnt even solveable at all in a generic(as in one implementation not as in Java generics) way, since there could be new Number types etc.
@Stefan This is correct, you need to provide the "elementary addition operator" for each type that you would like to use with the generic adder. I would go even farther and say that Java has no real generics: all it has is a compile-time checker which becomes completely useless at runtime.
Seems like the best Java can do :-/ Such a shame. Thank you for the answer.
|
3

As Number class does not expose interface for performing calculations, the only way to solve this problem is to create classes which encapsulates required operations for each supported numeric type. Than in your class you will need to use specific type.

Comments

2

Number has intValue(), floatValue(), doubleValue(), longValue, and shortValue(). Choose one and use it. For example,

double total;
total += number.doubleValue();

return total;

Also, java generics are in no way equivalent to c++ templates. You can not allocate new instances of a java generic type. This can never work:

E hoot = (E) new Object();

Finally, long, short, int, double, and float are not class types; they are primitive types. As such they are not available for use with Java generics.

1 Comment

What about BigDecimal(it is a number too)? Neither function will return the 'right' value in alot of cases.
2

below method get numbers such as int, float, etc and calculate sum of them.

@SafeVarargs
private static <T extends Number> double sum(T... args) {
    double sum = 0d;
    for (T t : args) {
        sum += t.doubleValue();
    }
    return sum;
}

Comments

1

Number doesn't support any operations so you need to cast the values to the types required. e.g. If Number is a BigDecimal, there is no += operator.

Comments

0

You should check runtime type (e.g. using instanceof) and cast to the known type and do appropriate addition operation. Not sure what will be type of result, taking in account that the list could contain a lot of different number types.

1 Comment

And possibly even unknown(at the time of writing) Number types.
0

since the introduction of Java 8 streams and lambda you can have a shorter solution

public interface Adder {

    static <E extends Number> E sumValues(Collection<E> objectsToSum, BinaryOperator<E> sumOp) {
        return objectsToSum.stream().reduce(sumOp).orElse(null);
    }
}

and use it as follows

int sum = Adder.sumValues(List.of(4, 5, 6, 7), Integer::sum);

Note, that Stream::reduce with just accumulator returns Optional that's why I used orElse(null), but I would recommend to send also zero value as parameter to Adder::sumValue

Comments

0

not elegant, but works

public class GenericSum {

    public static <T extends Number> Number sum(T x, T y) throws Exception{
        // Field
        Field primitiveField = x.getClass().getField("TYPE");
        // int|float... object
        var primitiveType = primitiveField.get(null);
        // cast to class
        var adder = x.getClass().getMethod("sum", (Class)primitiveType,(Class)primitiveType);
        var result = adder.invoke(null,x, y);
        return (Number) result;
    }

    public static void main(String[] args) throws Exception {
        var a1 = 3;
        var a2 = 5;

        var res = sum(a1, a2);
        System.out.println(res);

        var b1 = 2.0f;
        var b2 = 3.0f;
        var res2 = sum(b1,b2);
        System.out.println(res2);

    }

}

Comments

0

I was looking for any implementation of a generic adder accumulator when I came across this question.

I feel this is a lot messy and ugly for most use cases but if you need a very generic one here it is.

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.function.BinaryOperator;

public class Adder implements BinaryOperator<Object> {
    @Override
    public Object apply(Object partialSum, Object element) {
        // if both are instances of Number, then add them
        // otherwise we parse the element as string
        // and add it to the partial sum
        if(partialSum instanceof Number) {
            if(element instanceof Number) {
                if(partialSum instanceof Integer)
                    return (Integer) partialSum + ((Number) element).intValue();
                if(partialSum instanceof Long)
                    return (Long) partialSum + ((Number) element).longValue();
                if(partialSum instanceof BigInteger)
                    return ((BigInteger) partialSum).add(BigInteger.valueOf(((Number) element).longValue()));
                if(partialSum instanceof Float)
                    return (Float) partialSum + ((Number) element).floatValue();
                if(partialSum instanceof Double)
                    return (Double) partialSum + ((Number) element).doubleValue();
                if(partialSum instanceof BigDecimal)
                    return ((BigDecimal) partialSum).add(BigDecimal.valueOf(((Number) element).doubleValue()));
                else
                    throw new NumberFormatException("Unknown number type for partialSum: " + partialSum.getClass());
            }
            else {
                if(partialSum instanceof Integer)
                    return (Integer) partialSum + Integer.parseInt(element.toString());
                if(partialSum instanceof Long)
                    return (Long) partialSum + Long.parseLong(element.toString());
                if(partialSum instanceof BigInteger)
                    return ((BigInteger) partialSum).add(new BigInteger(element.toString()));
                if(partialSum instanceof Float)
                    return (Float) partialSum + Float.parseFloat(element.toString());
                if(partialSum instanceof Double)
                    return (Double) partialSum + Double.parseDouble(element.toString());
                if(partialSum instanceof BigDecimal)
                    return ((BigDecimal) partialSum).add(new BigDecimal(element.toString()));
                else
                    throw new NumberFormatException("Unknown number type for partialSum: " + partialSum.getClass());
            }
        }
        throw new NumberFormatException("partialSum " + partialSum + " must be of type java.lang.Number but found " + partialSum.getClass());
    }
}

Honestly this would've been a lot simpler if generic Number types supported the + operator like how Strings does.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.