Java: Generic methods and numbers

2019-01-09 03:26发布

问题:

I want to make a generic method which makes the total sum of a List of numbers.

What I was trying is this:

public static <T extends Number> T sumList(List<T> data)
{
    T total = 0;
    for (T elem : data)
    {
        total += elem;
    }
    return total;
}

But the problem is that there is no += operator in T and that total can't be assigned to zero.

How can I do this?

Thanks

回答1:

There are ways you can hack this together but in all honestly, generics is simply not the way to go here. Build a method for each concrete primitive wrapper type and implement them separately. It'll be way too much of a headache to make it generic; arithmetic operations can't happen generically.

You don't really gain anything by making it generic, either. It's such simple and constant code that you aren't worried about code duplication, since it's not going to change. And people aren't going to be passing in their own type of Number to your code; the domain of types it applies to is already well defined and finite.



回答2:

I've done that recently (based on lambdaj code) beware it will need all your elements to be the same type (you can't really add a Byte and a BigDecimal) and can throw a CCE if it isn't the case and won't handle custom Number:

public class SumAggregator<T extends Number> {
    public T aggregate(Iterable<T> iterable) {
        T result = null;
        for (T item : iterable) {
            result = aggregate(result, item);
        }
        return result;
    }


    @SuppressWarnings("unchecked")
    protected T aggregate(T first, T second) {
        if (first == null) {
            return second;
        } else if (second == null) {
            return first;
        } else if (first instanceof BigDecimal) {
            return (T) aggregate((BigDecimal) first, (BigDecimal) second);
        } else if (second instanceof BigInteger) {
            return (T) aggregate((BigInteger) first, (BigInteger) second);
        } else if (first instanceof Byte) {
            return (T) aggregate((Byte) first, (Byte) second);
        } else if (first instanceof Double) {
            return (T) aggregate((Double) first, (Double) second);
        } else if (first instanceof Float) {
            return (T) aggregate((Float) first, (Float) second);
        } else if (first instanceof Integer) {
            return (T) aggregate((Integer) first, (Integer) second);
        } else if (first instanceof Long) {
            return (T) aggregate((Long) first, (Long) second);
        } else if (first instanceof Short) {
            return (T) aggregate((Short) first, (Short) second);
        } else {
            throw new UnsupportedOperationException("SumAggregator only supports official subclasses of Number");
        }
    }

    private BigDecimal aggregate(BigDecimal first, BigDecimal second) {
        return first.add(second);
    }

    private BigInteger aggregate(BigInteger first, BigInteger second) {
        return first.add(second);
    }

    private Byte aggregate(Byte first, Byte second) {
        return (byte) (first + second);
    }

    private Double aggregate(Double first, Double second) {
        return first + second;
    }

    private Float aggregate(Float first, Float second) {
        return first + second;
    }

    private Integer aggregate(Integer first, Integer second) {
        return first + second;
    }

    private Long aggregate(Long first, Long second) {
        return first + second;
    }

    private Short aggregate(Short first, Short second) {
        return (short) (first + second);
    }
}

This code executed on ideone with examples.



回答3:

You can use a library, such as Generic Java Math (its numbers part), which provides generic operations for Number subclasses. It provides a way to work generically with numbers, just as in your example, but with passing Arithmetics object around, that does the actual calculation. See example on the home page.
It must use boxed types, so if you need speed, no generic implementation can do that in Java, you must use primitive types.



回答4:

Have a look at following example of generic method for summation of any Number type of variable lists... I am sure that this is what you was asking for..

Mark Peters is also right in his answer, but this also is a solution to go for.

public class GenericSum {
@SuppressWarnings("unchecked")
public static <E> E sumArray(E[] inputArray) {
    Double result = 0.0;
    // Sum array elements
    for (E element : inputArray) {
        result = ((Number) result).doubleValue()
                + ((Number) element).doubleValue();
    }
    return ((E) result);
}

public static void main(String args[]) {
    // Create arrays of Integer, Double
    Integer[] intArray = { 1, 2, 3, 4, 5 };
    Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };

    System.out.println("Sum of doubles : " + sumArray(intArray));
    System.out.println("Sum of doubles : " + sumArray(doubleArray));
}

}