Gabriele Picco
Gabriele Picco

Reputation: 537

Return type of generic method (Java)

I have this generic function:

public static <T extends Number> T sum(List<T> list){
    Number tot = 0;
    for(Number n: list){
        tot = tot.doubleValue() +  n.doubleValue();
    }
    return (T)tot;
}

Recall from the main with this code:

public static void main(String[] args) {
    ArrayList<Integer> listInt = new ArrayList<>();
    listInt.add(3);
    listInt.add(5);
    listInt.add(6);
    listInt.add(8);
    System.err.println("Sum: " + Calcolatrice.sum(listInt))
}

I therefore expect (being listInt an ArrayList of integers) that the value returned by the function sum is T = Integer and that in this case, give me a conversion error from Double to Integer. The type of the result is instead Double and no error is thrown. The cast (T)tot does not have the desired result.

I suppose it's because Java handles generics stance phase, but someone knows better explain why it works this way?

Upvotes: 6

Views: 176

Answers (3)

Frank Wilson
Frank Wilson

Reputation: 3250

The return type of Calcolatrice.sum(listInt) is actually Integer thanks to the cast at the end of sum in your example. However the real type of the result is Double. Obviously this is not a good situation, but it happened because you explicitly told the compiler in the cast that the return value would have the same type as the return value.

This situation means that if you were to instead write Calcolatrice.sum(listInt).toString() you would get a ClassCastException: java.lang.Double cannot be cast to java.lang.Integer because you would be calling Integer.toString() on a Double. What your code is actually doing is System.out.println("Sum: "+ String.valueOf(Calcolatrice.sum(listInt))) which works because it downcasts to Object (thus resolving the conflict) before calling toString.

As @TmTron points out you cannot cast between Double and Integer boxed types in the same way that you can do with primitives.

i.e.

int i = (int) 2.0;

follows different rules from:

Integer i = (Integer) new Double(2.0)

Upvotes: 1

TmTron
TmTron

Reputation: 19381

When you look at the type hierarchy you see that both Integer and Double are children of Number: so they are siblings and you cannot directly cast Double to Integer:

    Double testDbl = 3.14;
    Integer testInt = (Integer)testDbl; // this will NOT compile

The compiler cannot know for sure, if the cast in the return statement of your sum function will work or fail: but the compiler will show a warning:

Unchecked cast: 'java.lang.Number' to 'T'

Because of the hard-cast (T)tot, the compiler will simply take it for granted that your function returns an Integer (which it does not).

And finally, because of type erasure, there is no runtime check.

Upvotes: 3

Guillaume F.
Guillaume F.

Reputation: 6463

The Generics are only used at compilation to make sure you are not making mistakes in your code : Here, the parameter is List<T>, and the receiving variable must be of type T. That's all.

During runtime, T doesn't exist, and tot is a not an instance of T; it's a Number.

You can use T inside your method to receive the undefined instances but, for example, you can't make a new T().

Upvotes: 3

Related Questions