P08 · ClassCastException:类型强转翻车,Integer 变不成 String
场景还原:一段从老项目继承来的代码,用"原始类型 List"装数据(为了省事没写泛型)。后来你接手,把它当成
List<String>用——结果运行时炸了:
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String编译的时候好好的,一运行就崩。这正是泛型擦除给你埋的雷:编译期检查的"类型安全",到了运行期变成了"信不信由你"。
一、问题现象
注: CastDemo.java使用了未经检查或不安全的操作。 ← 编译时就警告你了 第一个元素: 你好 Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap') at CastDemo.main(CastDemo.java:14)二、出错代码
importjava.util.*;publicclassCastDemo{publicstaticvoidmain(String[]args){List<String>list=newArrayList<>();// 老代码 / 反射 / JSON 反序列化里容易混入"原始类型"用法:Listraw=list;// 原生类型 List(没写泛型)raw.add("你好");raw.add(123);// ❌ 往"以为是 String"的列表里塞了 IntegerStringfirst=list.get(0);// 没问题,"你好"本来就能转成 StringSystem.out.println("第一个元素: "+first);Stringsecond=list.get(1);// ← 这里炸了:Integer 无法强转成 StringSystem.out.println("第二个元素: "+second);}}三、为什么会这样
核心原因一句话:Java 的泛型在运行期会被"擦除"。
List<String> list里的<String>只在编译期起作用——编译器用它做类型检查(往里面 add 一个 Integer 在编译期会被拦住)。但编译成字节码后,这个类型信息就没了,运行期的List就是一个"什么都能装的桶"。
于是这段代码干了这么件事:
- 先用原生类型
List raw = list绕过了编译期检查,成功把Integer 123塞了进去; - 运行期
list.get(1)返回的其实是Object(指向 Integer 123); - 代码里声明了
String second = ...,JVM 在赋值时悄悄插入了强转(String); - 一个 Integer 怎么可能变成 String?→
ClassCastException。
谁最常踩这个坑:
- 老代码 / 历史遗留的"原始类型集合";
- JSON 反序列化(Gson/Jackson 没指定泛型时,反序列化出的 List 里元素类型不可信,取出来强转随时炸);
- 反射调用返回 Object 后随手强转;
- 泛型与继承混用(
List<String>不是List<Object>的子类这类问题)。
四、怎么解决
原则:让类型从源头就干净,别把"类型安全"寄托在运行期的强转上。
方案一:杜绝原始类型,全程写泛型:
List<String>list=newArrayList<>();list.add("你好");// list.add(123); // ✅ 编译期直接报错,根本活不到运行期方案二:从"不可信来源"(JSON/反射)取数据时,先校验再强转:
Objectobj=map.get("age");// JSON 解析出来的 Objectif(objinstanceofInteger){// ✅ 先 instanceof 判断intage=(Integer)obj;}else{// 类型不对,走兜底/报错,而不是直接崩溃System.out.println("age 字段类型不是数字: "+obj);}方案三:JSON 反序列化一定带上明确的泛型:
// Gson:TypeToken 保住泛型List<User>users=newGson().fromJson(json,newTypeToken<List<User>>(){}.getType());// Jackson:TypeReference 同理List<User>users2=objectMapper.readValue(json,newTypeReference<List<User>>(){});五、防坑总结
- 代码里出现"裸 List/Map"(没泛型)就是隐患,尽早补上泛型;
- 编译器的"未经检查的操作"警告别忽略,它是在提醒你这里类型不安全;
- 从 JSON/反射拿到 Object 先
instanceof再强转; - 集合别跨"类型假设"传递(一个 List 被当两种类型用,早晚出事);
- 强转失败看异常信息:
X cannot be cast to Y,说的是 X 转不成 Y——去源头查谁把 X 塞进来的。
下一篇:P09 · 数字转换 NumberFormatException