Java泛型类不能直接创建T[],因类型擦除导致运行时无法确定T的具体类型;必须用Object[]作底层存储,并通过安全的类型转换和API设计保障类型安全。

泛型类内部不能直接创建 T[],因为 Java 在运行时存在类型擦除——泛型参数 T 会被抹去,JVM 不知道它具体是 String、Integer 还是其他类型,所以 new T[size] 是编译错误。主流做法是用 Object[] 作为底层缓冲区,再通过“类型安全的向下转型”对外提供 T 视图,关键在于:**不暴露原始 Object[],也不在返回元素时做不加检查的强转**。
为什么必须用 Object[] 做底层数组
这是 JVM 类型系统的硬性限制。泛型信息仅存在于编译期,运行时所有 List<string></string>、List<integer></integer> 都退化为 List,数组的组件类型却必须在运行时确定(如 String[].class 和 Integer[].class 是不同类)。因此:
-
new T[10]❌ 编译失败:无法实例化泛型数组 -
new Object[10]✅ 合法,且能存放任意引用类型元素 - 后续所有对
T的“假装”都建立在开发者对类型使用契约的保证之上
如何安全地从 Object[] 取出 T 类型元素
不能写 (T) elements[i] 就完事——这会触发 unchecked cast 警告,且一旦误存了非 T 类型对象(比如本该存 String 却塞入 Integer),运行时才崩。正确做法是:
- 对外只提供
get(int index): T方法,内部用elements[index]获取,但不立即转型 - 若需校验(如调试或强约束场景),可在
set时检查:if (value != null && !typeErasedClass.isInstance(value)) throw new ClassCastException(...);其中typeErasedClass可通过构造时传入Class<t></t>得到 - 更轻量的做法是依赖调用方守约——就像
ArrayList那样,默认信任add(T)传入的是合法类型,把类型安全边界放在 API 入口,而非每次 get 都检查
如何对外提供“看起来是 T[]”的视图
用户有时需要 T[](比如传给某个只接受 String[] 的旧 API)。这时不能直接返回 (T[]) elements,而应:
- 提供一个带参数的
toArray(T[] a)方法,语义与ArrayList.toArray(T[])一致 - 若
a足够长,直接填充并返回;若不够,新建T[](利用a.getClass()获取运行时数组类型)再复制 - 避免无参
toArray()返回Object[],防止用户误强转——这点要明确写在 Javadoc 中
为什么不建议在构造时传 Class 来全程做运行时类型检查
虽然可行(如 new MyList<string>(String.class)</string>),但代价明显:
- 值类型(
int、boolean)无法传对应Class,只能用包装类,带来装箱开销 - 每次
add都要调用clazz.isInstance(obj),性能下降显著 - 破坏泛型设计初衷:类型安全应在编译期捕获,而非靠运行时补救
- 实际工程中,
ArrayList、LinkedList等标准类均未采用此方案,而是靠 API 设计+文档约定保障

















