我正在尝试理解看似类似代码的不同编译结果。似乎可以将List<Object>
转换为List<T>
,但前提是T
不受具体类限制。所以给...
List<Object> bis = new ArrayList<>();
以下内容在class Test<T>
和class Test<T extends CharSequence>
进行编译,但不在class Test<T extends BigDecimal>
和class Test<T extends BigDecimal & CharSequence>
进行编译。
List<T> result = (List<T>) bis;
那么,与类型和接口类型有关的T
的区别在哪里呢?
修改:
每个请求都有一些完整的代码。这在第9行编译了一个编译器警告。对于result
的项目进行操作确实不安全,好像它们是CharSequence
的实例导致ClassCastException
。
public class Sandbox<T extends CharSequence> {
public static void main(String[] args) {
new Sandbox<CharSequence>().foo();
}
private void foo() {
List<Object> bis = Arrays.asList(Integer.valueOf(1));
List<T> result = (List<T>) bis;
System.out.println(result);
}
}
然而,这根本不编译:
public class Sandbox<T extends BigDecimal> {
public static void main(String[] args) {
new Sandbox<BigDecimal>().foo();
}
private void foo() {
List<Object> bis = Arrays.asList(Integer.valueOf(1));
List<T> result = (List<T>) bis;
System.out.println(result);
}
}
第9行中的编译器错误:无法将List<Object>
强制转换为List<T>
。
答案 0 :(得分:0)
这个List<Object> bis = Arrays.asList(Integer.valueOf(1));
显然无法编译,因为Arrays.asList(Integer.valueOf(1))
会返回List<Integer>
,这就是你获得Cannot cast List<Object> to List<T>
的原因。
第二种情况,eclipse荒谬地用T extends CharSequence
编译,但我用在线编译器检查它并没有编译,所以它很可能是一个日食错误。
答案 1 :(得分:0)
理解此类行为的关键是了解类型擦除实际上做了什么。尽管人们普遍认为它在运行时删除了类型参数 - 但实际上并不是这样。它的作用 - 它将减少到声明的参数范围。因此,只要您键入List<T>
T
已绑定的任何位置,它实际上会缩减为List<[lower-bound-of-T]>
。由于Object
不是CharSequence
,List<Object>
无法投放到List<T>
(因为它至少意味着List<CharSequence>
)
为什么会这样?想象一下,您的Sandbox
类具有接受T类型参数的方法:
public class Sandbox<T extends CharSequence> {
void bar(T param) {
//...
}
}
bar(T)
可以接受T
,而T
是任何扩展CharSequence
的内容,所以基本上在运行时这有效地用作
void bar(CharSequence param) {
//...
}
因此它无法接受简单的Object
。这就是为什么仅存在非空的较低参数限制来限制来自...<Object>
此外,通配符?
类型参数可能会让您大吃一惊。它说编译器类似“我不知道它是什么,但我很确定它满足所有界限”。非常奇怪,包括这个事实,你可以(几乎?)投射任何东西,所以它可以实现
List<Object> bis = Arrays.asList(Integer.valueOf(1));
List<T> result = (List<T>) (List<?>) bis;
因此,对于带有泛型的设计类,你应该非常非常小心。