《Java Generics and Collections》正确示例为何符合Truth in Advertising原则
核心差异:数组的实际运行时类型(具体化类型)不同
你对类型擦除的等效代码推导没有错,但你忽略了引用的静态类型和对象的实际运行时类型的区别,这是两个例子表现不同的核心原因。
错误示例的问题本质
第一个例子中你直接new Object[c.size()]创建数组,这个数组的实际运行时类型就是Object[],(T[])new Object[...]的强转只是骗过了编译器,运行时因为T擦除为Object,所以这个强转当时不会报错,但方法返回的数组本质就是Object[],完全不是调用方需要的String[],所以调用方处插入的(String[])强转必然失败,违反了「方法承诺返回T[],就真的返回T[]实例」的Truth in Advertising原则。
正确示例的运行逻辑
第二个例子的核心是使用了反射根据传入数组的组件类型创建数组:
- 你调用
toArray(strings, new String[0])时,传入的数组实际类型是String[],通过a.getClass().getComponentType()拿到的组件类型就是String.class Array.newInstance(String.class, size)创建出来的数组实际运行时类型就是String[],只是因为方法返回值是Object,所以需要强转成(T[]),这个强转虽然会触发unchecked警告,但事实上是合法的,因为创建的确实是T类型的数组- 擦除后方法返回的是
Object[]类型的引用,但这个引用指向的实际对象是String[],Java中把指向String[]实例的Object[]引用强转为String[]是完全合法的操作,和Object o = "test"; String s = (String)o;的逻辑完全一致,自然不会抛出ClassCastException。
两个(T[])强转的本质区别
- 错误示例的
(T[])new Object[...]:强转的对象实际类型就是Object[],和T[]完全不兼容,只是擦除后编译器没法在方法内检查,把报错推迟到了调用方 - 正确示例的
(T[])Array.newInstance(...):强转的对象实际类型就是T[],只是编译器无法验证泛型的一致性,所以只报unchecked警告,运行时完全合法
这个正确示例完全符合Truth in Advertising原则:方法签名承诺返回T[],实际返回的数组运行时类型确实是T[],没有欺骗调用方。
内容的提问来源于stack exchange,提问作者crazyforMcCafe
相关产品推荐
相关产品推荐

