Java中未检查类型转换的隐患:从List<Map>到List<Student>的映射问题解析

devtools/2025/2/9 12:20:29/

为什么你的Java对象中出现未知属性?

  • 问题出现
  • 原因
    • 1. 类型擦除与未检查的类型转换
    • 2. 根本原因:Map到Student的映射缺失
  • 为什么代码没有抛出异常?
  • 解决方案:显式映射Map到Student
      • 方案1. 手动转换
      • 方案2:使用对象映射框架(推荐)
    • 添加泛型检查

问题出现

我在运行这段代码的时候,发现了一个非常奇怪的点。

java">List<Student> Students = (List<Student>) insertMap.get("Student")

首先

  1. insertMap.get("Student") 得到的类型是 List<Map> 类型
  2. 但是这段代码可以运行!
  3. 但是,当我debug时,发现 Students 这了列表集合中子元素并不是Student!而是Map类型。里面出现了非Student类的属性

原因

问题的根源是类型转换未校验Map到对象的映射缺失。通过显式的数据映射(手动或通过框架),可以确保Student对象的属性与数据源一致。

1. 类型擦除与未检查的类型转换

Java的泛型在编译后会经历类型擦除,这意味着List在运行时实际是List<Object>。当你强制将List<Map>转换为List<Student>时,编译器无法在运行时检查元素类型是否匹配。例如:

java">List<Map> rawList = (List<Map>) insertMap.get("Student");
List<Student> students = (List<Student>) rawList; // 此处强制类型转换是危险的!

2. 根本原因:Map到Student的映射缺失

问题核心在于:你没有显式地将Map中的数据转换为Student对象。即使类型转换通过,List中的元素实际仍是Map类型,但这些Map被错误地当成了Student对象。当你试图访问Student的属性时,JVM会尝试从Map中查找与Student字段同名的键值,导致以下问题:

  • 存在不属于Student类的属性:因为Map中可能包含其他键,例如Map有一个address字段,而Student类没有该字段。
  • 属性值类型不匹配:例如Map中的age是字符串类型,而Student的age是整型。

为什么代码没有抛出异常?

强制类型转换在以下情况下不会抛出异常:

  • 运行时的 List 元素类型(Map)和目标元素类型(Student)不被检查。Java 不会对泛型类型进行运行时检查,只会检查容器本身是否为 List 类型。
  • 如果你尝试访问 Student 的方法(如 getName),而 Map 中的内容能够支持这种访问(通过类似反射或动态代理机制),程序可能不会立即出错。

但是,这种行为是一种未定义的运行时行为,可能会导致难以调试的错误。

java">Map<String, Object> map = new HashMap<>();
map.put("name", "Tom");
map.put("age", 18);
map.put("extra", "unexpected"); // 多出的属性

当你将 List 强制转换为 List 时,程序将把 Map 对象当作 Student 对象处理。如果你通过 Students.get(0).getName() 调用 getName() 方法,程序可能会尝试将 Map 的 get(“name”) 的结果返回给你。这实际上是因为 Map 和 Student 混淆了,而不是因为 Map 真正是一个 Student。

因此:

  • 如果 Map 中的键值对恰好与 Student 的属性名一致(如 “name” 和 “age”),程序可能会表现得像是正常运行。
  • 如果 Map 中有额外的键值对(如 “extra”),它们不会被 Student 类直接识别,但它们仍然存在于底层对象中,造成了“有不属于 Student 的属性”的现象。

解决方案:显式映射Map到Student

你需要手动将Map中的数据转换为Student对象。以下是两种常见方法:

方案1. 手动转换

java">List<Map<String, Object>> mapList = (List<Map<String, Object>>) insertMap.get("Student");
List<Student> students = new ArrayList<>();for (Map<String, Object> map : mapList) {Student student = new Student();student.setId((String) map.get("id"));student.setName((String) map.get("name"));// 显式映射其他字段...students.add(student);
}

方案2:使用对象映射框架(推荐)

如果项目中已引入Jackson或Gson,可以直接将Map序列化为JSON,再反序列化为Student对象:

java">
import com.fasterxml.jackson.databind.ObjectMapper;ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> mapList = (List<Map<String, Object>>) insertMap.get("Student");
List<Student> students = mapList.stream().map(map -> mapper.convertValue(map, Student.class)).collect(Collectors.toList());

添加泛型检查

如果你需要确保类型安全,可以在获取数据时添加泛型检查,避免直接使用未经检查的类型转换:

java">Object obj = insertMap.get("Student");
if (obj instanceof List<?>) {List<?> list = (List<?>) obj;if (!list.isEmpty() && list.get(0) instanceof Map) {// 进行类型转换}
}

http://www.ppmy.cn/devtools/157350.html

相关文章

【安当产品应用案例100集】037-强化OpenVPN安全防线的卓越之选——安当ASP身份认证系统

在当前数字化时代&#xff0c;网络安全已成为企业发展的重要组成部分。对于使用OpenVPN的企业而言&#xff0c;确保远程访问的安全性尤为重要。安当ASP身份认证系统凭借其强大的功能和便捷的集成方式&#xff0c;为OpenVPN的二次登录认证提供了理想的解决方案&#xff0c;特别是…

《机器学习数学基础》补充资料:秩-零化度定理

在拙作《机器学习数学基础》中&#xff0c;对于机器学习直接相关的线性代数的内容做了比较详细的讲解&#xff0c;但是&#xff0c;由于书中是以“机器学习”为核心&#xff0c;而非“线性代数”&#xff0c;所以对其中的更基本的内容没有深入探究。为了让有兴趣深入学习的读者…

Java 集合中的 `removeIf` 和 Stream API 的 `filter`

前言 在Java编程中&#xff0c;处理集合数据是一项常见的任务。为了更高效地过滤集合中的元素&#xff0c;Java 8引入了两种强大的方法&#xff1a;removeIf 和 Stream API 中的 filter 方法。 一、removeIf 方法 1. 工作原理 removeIf 是 Collection 接口中的一个默认方法&…

SQLite3实战教程:从入门到精通

SQLite是一个轻量级的关系型数据库,广泛应用于移动应用和小型Web应用。本教程将带您深入了解SQLite3,学习如何在Django项目中使用它,并掌握相关的数据库管理命令。 1. SQLite3基础 1.1 什么是SQLite? SQLite是一个嵌入式关系型数据库引擎,具有以下特点: 无需单独的服务器进…

技术晋升读书笔记—人月神话

“人月”可以互换&#xff1f; “九个女人能一个月生下一个孩子&#xff1f;” “向延期的软件项目&#xff0c;临时增加人手&#xff0c;能快速完成&#xff1f;” 《人月神话》这本书堪称软件工程领域的经典之作。弗雷德里克布鲁克斯通过一系列精辟的论述&#xff0c;揭示…

前端页面如何兼容不同的分辨率

目录 1.兼容元素 2. 方案 2.1. 字号 2.2. 布局 2.3. 图片 3. 效果 4. 优势 1.兼容元素 想要让一个项目在不同的分辨率适配,需要首先了解需要适配的内容 需要兼容元素主要包括: 字号布局图片下面拿某一个大屏项目作为例子讲解。 2. 方案 2.1. 字号 首先说字号部分,…

重学SpringBoot3-整合 Elasticsearch 8.x (二)使用Repository

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞??收藏评论 整合 Elasticsearch 8.x &#xff08;二&#xff09;使用Repository 1. 环境准备 1.1 项目依赖1.2 Elasticsearch 配置 2. 使用Repository的基本步骤 2.1 创建实体类2.2 创建 Reposit…

【PyQt】集中式样式表(QSS文件)管理界面样式

集中式样式表&#xff08;QSS文件&#xff09;管理界面样式 集中式样式表&#xff08;通常使用QSS&#xff0c;Qt StyleSheet&#xff09;是一种非常有效的方式来管理和定制你的PyQt应用程序的界面样式。类似于Web开发中的CSS&#xff0c;QSS允许你以声明式的方式定义组件的外…