假设我有以下内容:
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)
答案 0 :(得分:7)
正是您所说的原因,类型参数实际上可能是您传入的对象的实际运行时类型的超类型! T#getClass()
不会返回Class<T>
,而会返回Class<? extends T>
Number number = Integer.valueOf(1);
List<Number> list = first(number);
在运行时调用n.getClass()
时,将返回Integer.class
,而不是Number.class
,但您尝试将结果分配给List<Number>
!编译器无法知道真正的运行时类型是什么,它最多只知道List<? extends Number>
返回。强迫你投入演员是一种说法:“我不能担保这项操作的安全性,这是你的话,这是正确的。”
任何时候编译器都无法确认操作是否属于类型安全,它将强制您进行投射并因此产生“未经检查”的警告,以便它完成其让您知道该问题的工作。
答案 1 :(得分:4)
getClass()
返回Class<?>
类型的值。当您将其传递给first
的另一个覆盖时,您正在执行未经检查的强制转换为Class<? extends Widget>
。它是未经检查的,因为泛型在运行时使用“类型擦除”,这意味着JVM无法检查它;通过-Xlint:unchecked
查看此警告。
请注意,这不是Class<T extends Widget>
,这意味着类型系统不能确保此方法的类型参数(first
的第一个覆盖)与被调用的类型参数的类型参数相同(另一个first
)。
因此,您返回的结果是List<? extends Widget>
类型(其中? extends Widget
是capture#1
),这与List<T extends Widget>
不兼容,因此编译器正确生成错误。
在这种情况下,您碰巧知道(尽管编译器没有)这是合理的事情,因此您可以使用显式强制转换来覆盖它。但在这种情况下,为什么不这样做呢:
public <T extends Widget> List<T> first() {
return new ArrayList<T>();
}
答案 2 :(得分:3)
这是因为n.getClass()
始终返回Class<?>
而不是Class<T>
,因此编译器无法直接解析此问题。你总是需要在这里明确地施放。例如
public <T extends Widget> List<T> first(T n) {
return first((Class<T>)n.getClass());
}
答案 3 :(得分:1)
泛型及其各自的最终类型是在编译时确定的,而不是在运行时确定的。在运行时,所有通用信息都将被删除 - 这称为Type erasure。 看来你在这里试图确定函数调用时的泛型类型,这在Java中是不受支持的
public <T extends Widget> List<T> first(T n) {
return first(n.getClass());
}
在这里,您需要返回List<T>
- 列表实例。具体实例必须具有具体参数T
。即一个人不能返回List<T extends Widget>
- 类型将是未知的。你会在这样的清单中存储什么?
public <T extends Widget> List<T> first(Class<T> n) {
return new ArrayList<>();
}
在这里,您希望根据提供的类返回一个列表 - 但是看到您实际上正在创建一个通用的ArrayList,然后将其转换为所需的类型。问题是这里的T与前一种方法中的T不同。你需要用一个T来声明整个类的泛型,以使它们相同。