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?

adarshr
  • 61,315
  • 23
  • 138
  • 167
David M. Coe
  • 309
  • 1
  • 6
  • 14
  • 1
    What if you have a list which contains different Number subclasses? E.g. `Arrays.asList(5L, 1e100, 42)`? – kan Dec 29 '11 at 15:40
  • Duplicate of http://stackoverflow.com/questions/285754/java-using-generics-to-implement-a-class-that-operates-on-different-kinds-of-nu – Skip Head Dec 29 '11 at 15:45
  • @kan - I would expect I'd get an error since they're not all of type E, right? – David M. Coe Dec 29 '11 at 16:45
  • @DavidM.Coe Suppose that E == Number. So, which error do you expect? – kan Dec 29 '11 at 16:55

9 Answers9

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);
    }
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • +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 Dec 29 '11 at 17:36
  • @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. – Sergey Kalinichenko Dec 29 '11 at 17:42
  • 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 Dec 29 '11 at 18:10
  • 1
    @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. – Sergey Kalinichenko Dec 29 '11 at 18:15
  • Seems like the best Java can do :-/ Such a shame. Thank you for the answer. – David M. Coe Jun 28 '12 at 18:56
  • @dasblinkenlight how can you create an instance of interface,or what does this `new adder()` exactly translate to – brain storm Aug 16 '13 at 03:24
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.

Novakov
  • 3,055
  • 1
  • 16
  • 32
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;
}
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.

DwB
  • 37,124
  • 11
  • 56
  • 82
  • What about BigDecimal(it is a number too)? Neither function will return the 'right' value in alot of cases. – Stefan Dec 29 '11 at 17:34
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.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
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

Adrian
  • 2,984
  • 15
  • 27
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);

    }

}

liunix
  • 11
  • 1
  • 2
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.

Souyama
  • 116
  • 2
  • 10
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.

kan
  • 28,279
  • 7
  • 71
  • 101