Harshal
Harshal

Reputation: 55

Overcome type erasure in Java

I have a client class which I cannot change.

List<Integer> list1= Arrays.asList(1,2,3);
System.out.println("Total sum is:"+sum(list1));
printlist(list1);
List<Double> list2=Arrays.asList(1.0,2.0,3.0);
System.out.println("Total sum is:"+sum(list2));
printlist(list2);

I have the business logic here

private static Object sum(List<? extends Number> list) {
    double sum = 0;
    for (Number i: list) {
        sum+=i.doubleValue();
    }     
    return sum;
}

So I want to return 6 for integer and 6.0 for a double. How can I do that? I am thinking to typecast sum as int or double based on type but due to type erasure all the info is lost. Can someone help me?

Upvotes: 0

Views: 336

Answers (3)

Jai
Jai

Reputation: 8363

It is dangerous to assume that a Number subclass instance can be represented with double. To do this safely, you need to check for all possible types.

@SuppressWarnings("unchecked")
private static <T extends Number> T sum(List<T> list) {
    Objects.requireNonNull(list);

    if (list.isEmpty() || list.contains(null))
        throw new IllegalArgumentException();

    Class<T> clazz = (Class<T>) list.get(0).getClass();

    if (clazz == Integer.class) {
        return (T) (Integer) list.stream().mapToInt(i -> (Integer) i).sum();
    }
    else if (clazz == Double.class) {
        return (T) (Double) list.stream().mapToDouble(i -> (Double) i).sum();
    }
    /* And other Number subclass types */

    // Not possible if all types covered
    throw new IllegalStateException();
}

Here is a question/answer related to this.

Upvotes: 0

Alex Taylor
Alex Taylor

Reputation: 8813

The objects in the list still have type information associated to them at run time. The only erased types are those of the generics (i.e. List in your example). The generics are checked at compile time, but are not maintained in the generated bytecode. That means you can use instanceof to check the contents of the collection:

private static Object sum(List<? extends Number> list) {
    Integer integerSum = 0;
    Double doubleSum = 0.0;
    boolean hasDouble = false;
    for (Number i: list) {
        if (i instanceof Integer) {
            integerSum += i.intValue();
        } else {
            doubleSum += i.doubleValue();
            hasDouble = true;
        }
    }
    if (hasDouble)
        return doubleSum + integerSum;
    return integerSum;
}

The code has some peculiarities to handle mixed lists, and doesn't handle Long, Short, Byte etc. properly.

Upvotes: 5

user10091428
user10091428

Reputation:

Use 'instanceof"

     if( i instanceof Double) {
       System.out.println("param is a Double");
     }
     else if( i instanceof Integer) {
       System.out.println("param is an Integer");
    }

Upvotes: 4

Related Questions