泛型方法的返回类型(Java)

6

我有这个通用函数:

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;
}

从这个代码中回忆起主函数:

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))
}

因此,我期望(listInt 是一个整数ArrayList)函数sum返回的值为T = Integer,并且在这种情况下,将Double转换为Integer时会出现转换错误。 结果类型实际上是Double,没有抛出错误。 强制转换(T)tot没有产生预期的结果。

我猜这是因为Java处理泛型的阶段问题,但其中有人更好地解释为什么会这样工作吗?


4
"type erasure"(类型擦除)是用来解释Java泛型中的行为,它指的是在编译时将泛型类型信息删除,使得运行时只能看到原始类型信息。 - ΦXocę 웃 Пepeúpa ツ
3个回答

3
泛型仅在编译时用于确保代码不会出错:这里的参数是 List<T>,接收变量必须是类型为 T。就是这样。
运行时,T不存在,tot 不是 T 的实例;它是一个 Number
您可以在方法中使用 T 来接收未定义的实例,但是例如,您不能创建一个 new T()

3
当您查看类型层次结构时,您会发现IntegerDouble都是Number的子类:因此它们是兄弟姐妹,您不能直接将Double转换为Integer
    Double testDbl = 3.14;
    Integer testInt = (Integer)testDbl; // this will NOT compile

编译器无法确定您的sum函数返回语句中的强制类型转换是否有效,但编译器会显示警告。
Unchecked cast: 'java.lang.Number' to 'T'

由于硬转换的 (T)tot,编译器会默认您的函数返回一个整数(实际上并非如此)。最后,由于类型擦除,没有运行时检查。

1
Calcolatrice.sum(listInt)返回类型实际上是Integer,这要归功于你在示例中对sum进行的转换。然而,结果的真实类型是Double。显然这不是一个好的情况,但发生了这种情况是因为你明确地告诉编译器,在强制类型转换中返回值将与返回值具有相同的类型。

这种情况意味着,如果你改为写Calcolatrice.sum(listInt).toString(),你将得到一个ClassCastException: java.lang.Double cannot be cast to java.lang.Integer,因为你会在一个Double上调用Integer.toString()。实际上,你的代码正在执行System.out.println("Sum: "+ String.valueOf(Calcolatrice.sum(listInt))),这是可行的,因为它在调用toString之前向下转型为Object(从而解决了冲突)。

正如@TmTron所指出的那样,你不能像原始类型那样在DoubleInteger封装类型之间进行转换。

例如:

int i = (int) 2.0;

遵循不同的规则:

Integer i = (Integer) new Double(2.0)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接