2017-03-04 29 views
6

我有这个通用功能:从主返回类型通用的方法的(Java)的

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)函数和返回的值是T = Integer,在这种情况下,给我一个从Double到Integer的转换错误。 结果的类型是Double而不会引发错误。 演员(T)tot没有预期的结果。

我想这是因为Java的泛型处理立场的阶段,但有人知道更好地解释为什么是这样工作的?

+4

[类型擦除](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html)是解释这种行为的术语.... –

回答

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

泛型仅在编译时用于确保您的代码中没有出错:此处的参数为List<T>,并且接收变量的类型必须为T。就这样。

在运行期间,T不存在,并且tot不是T的实例;这是一个Number

您可以在方法中使用T来接收未定义的实例,但是,例如,您不能创建new T()

3

当你看的类型层次,你看,这两个IntegerDoubleNumber儿:所以他们是兄弟姐妹,你不能直接投DoubleInteger

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

编译器不能肯定知道,如果在你的sum函数的返回语句中投将工作或失败:但是编译器将显示警告:

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

因为的硬编码(T)tot,编译器会简单地认为你的函数返回Integer(它没有)。

最后,由于type erasure,没有运行时检查。