为什么需要显式转换泛型调用?

2022-09-04 20:53:58

假设我有以下内容:

public <T extends Widget> List<T> first(T n) {
    return first(n.getClass());
}
public <T extends Widget> List<T> first(Class<T> n) {
    return new ArrayList<>();
}

编译器在第 3 行用 “” 进行报错。我不明白为什么。在我看来,除了子类型之外,该类型在任何一种情况下都不可能改变,这似乎是合理的。incompatible types; required: java.util.List<T>; found: java.util.List<capture#1 of ? extends my.app.Widget>T

这可以通过显式转换来解决,尽管我不知道为什么需要它。

public <T extends Widget> List<T> first(T n) {
    return (List<T>)first(n.getClass());
}
public <T extends Widget> List<T> first(Class<T> n) {
    return new ArrayList<>();
}

这可能是编译器错误吗?

注意:我使用的是 JDK 1.7.0_15:

java version "1.7.0_15"
Java(TM) SE Runtime Environment (build 1.7.0_15-b03)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)

答案 1

正是出于您所述的原因,type 参数实际上可能是您传入的对象的实际运行时类型的超类型! 不返回,它返回T#getClass()Class<T>Class<? extends T>

Number number = Integer.valueOf(1);
List<Number> list = first(number);

当您在运行时调用时将返回 ,而不是 ,但您正在尝试将结果分配给 !编译器无法知道真正的运行时类型是什么,它最多只知道返回的运行时类型。强迫你加入演员阵容是它说“我不能保证这次行动的安全性,这是你的话,这是正确的。n.getClass()Integer.classNumber.classList<Number>List<? extends Number>

每当编译器无法确认某个操作是类型安全的时,它就会强制您进行强制转换,从而产生“未选中”警告,以便它已完成让您了解问题的工作。


答案 2

getClass()返回类型的值。当您将其传递给 的另一个覆盖 时,您正在执行未经检查的强制转换到 。它是未选中的,因为泛型在运行时使用“类型擦除”,这意味着JVM无法检查它;通过以查看此警告。Class<?>firstClass<? extends Widget>-Xlint:unchecked

请注意,这不是,这意味着类型系统不确保此方法的类型参数(的第一个重写 )与被调用的方法(另一个)的类型参数相同。Class<T extends Widget>firstfirst

因此,您得到的结果是类型(where is ),它与 不兼容,因此编译器正确地产生了错误。List<? extends Widget>? extends Widgetcapture#1List<T extends Widget>

在这种情况下,您碰巧知道(尽管编译器不知道)这是一件合理的事情,因此您可以使用显式强制转换来覆盖它。但是在这种情况下,为什么不直接使方法如下:

public <T extends Widget> List<T> first() {
    return new ArrayList<T>();
}

推荐