1. 简介
在本篇短文中,我们将深入探讨 Java 中的 Raw Types(原始类型),了解它们的本质以及为何要尽量避免使用。
如果你写 Java 代码时还在用 List list = new ArrayList();
这种写法,那你可能正在使用原始类型。虽然它看起来简单,但背后隐藏了不少坑。
2. 什么是原始类型?
原始类型指的是 泛型类或接口在使用时未指定类型参数 的情况。例如:
List list = new ArrayList(); // 原始类型
而不是:
List<Integer> listIntgrs = new ArrayList<>(); // 参数化类型
在这里,List<Integer>
是接口 List<E>
的参数化类型,而 List
是其对应的原始类型。
原始类型在对接遗留非泛型代码时可能有用,但在现代 Java 开发中,不推荐使用。原因主要有以下几点:
✅ 表达性差
❌ 缺乏类型安全
❌ 运行时才暴露问题
3. 表达性差
原始类型无法像参数化类型那样清晰地传达其用途。比如:
List<String>
明确表示这是一个字符串列表;- 而
List
则什么信息都不提供。
我们来看 List
接口中的 get(int index)
方法定义:
E get(int index);
在参数化类型中,比如 List<String>
,这个方法返回的就是 String
。
但在原始类型中,它返回的是 Object
,你需要手动进行类型转换。
这就带来了额外的工作,也增加了出错的可能性。而且,这种错误不会在编译时被发现。
4. 缺乏类型安全
使用原始类型会退化到泛型出现前的 Java 行为:可以添加任何类型的对象。这在混合使用参数化类型和原始类型时特别危险。
举个例子:
public void methodA() {
List<String> parameterizedList = new ArrayList<>();
parameterizedList.add("Hello Folks");
methodB(parameterizedList);
}
public void methodB(List rawList) { // 原始类型!
rawList.add(1);
}
上面这段代码 可以编译通过(带警告),但运行时会在一个 List<String>
中插入了一个 Integer
。
编译器警告如下:
Note: RawTypeDemo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
5. 运行时问题频发
因为缺乏类型安全,这种错误往往在运行时才会暴露出来。
我们来改写上面的例子,在 methodA
中尝试获取第 1 个元素:
public void methodA() {
List<String> parameterizedList = new ArrayList<>();
parameterizedList.add("Hello Folks");
methodB(parameterizedList);
String s = parameterizedList.get(1); // 这里会出问题
}
运行时会抛出如下异常:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
这个错误本来可以在编译阶段被发现,但因为用了原始类型,直到运行时才暴露。
6. 总结
原始类型虽然语法简单,但存在严重的类型安全问题和运行时风险。它们:
- 不具备表达性,API 难以理解;
- 缺乏类型检查,容易混入错误数据;
- 错误延迟到运行时才暴露,排查成本高。
✅ 建议:始终使用参数化类型
❌ 避免使用原始类型,除非你真的在对接遗留代码
否则,你的代码可能在运行时“爆炸”,而你却毫无准备。