Effective Java笔记(33)优先考虑类型安全的异构容器
泛型最常用于集合,如 Set<E >和 Map<K ,V>,以及单个元素的容器 ,如 ThreadLocal<T>和 AtomicReference<T> 。 在所有这些用法中,它都充当被参数化了的容器 。 这样就限制每个容器只能有固定数目的类型参数。 一般来说 ,这种情况正是你想要的 。 一个 Set只有一个类型参数,表示它的元素类型; 一个 Map 有两个类型参数,表示它的键和值类型.......
但是,有时候你会需要更多 的灵活性 。 例如,数据库的行可以有任意数量 的列,如果能以类型安全的方式访问所有列就好了 。 幸运 的是,有一种方法可以很容易 地做到这一点 。这种方法就是将键( key )进行参数化而不是将容器( container )参数化 。 然后将参数化的键提交给容器来插入或者获取值 。 用泛型系统来确保值的类型与它的键相符 。
下面简单地示范一下这种方法:以 Favorites 类为例,它允许其客户端从任意数量的其他类中,保存并获取一个“最喜爱”的实例 。Class 对象充当参数化键的部分 。 之所以可以这样, 是因为类 Class 被泛型化了 。 类的类型从字面上来看不再只是简单 的 Class,而是 Class<T> 。 例如 ,String.class 属于 Class<String >类型,Integer.class属于 Class<Integer >类型 。 当一个类的字面被用在方法 中,来传达编译时和运行时的类型信息时,就被称作类型令牌。
Favorites 类的 API 很简单 。 它看起来就像一个简单 的映射 ,除了键(而不是映射)被参数化之外 。 客户端在设置和获取最喜爱 的实例时提交 Class 对象 。 下面就是这个 API:
public class Favorites {public <T> void putFavorite(Class<T> type, T instance);public <T> T getFavorite(Class<T> type);
}
下面是一个示例程序,检验一下 Favorites 类,它将保存、获取并打印一个最喜爱的 String 、Integer 和 Class 实例 :
public static void main(String[] args) {Favorites f = new Favorites();f.putFavorite(String.class, "Java");f.putFavorite(Integer.class, 0xcafebabe);f.putFavorite(Class.class, Favorites.class);String favoriteString = f.getFavorite(String.class);int favoriteInteger = f.getFavorite(Integer.class);Class<?> favoriteClass = f.getFavorite(Class.class);System.out.printf("%s %x %s%n", favoriteString,favoriteInteger, favoriteClass.getName()) ;
}
正如所料,这段程序打印出的是 Java cafebabe Favorites 。 注意,有时 Java 的printf 方法与 C 语言中的不同,C 语言中使用\n的地方,在 Java 中应该使用 %n 。 这个知会产生适用于特定平台的行分隔符,在许多平台上是\n,但是并非所有平台都是如此 。
Favorites 实例是类型安全( typesafe )的 :当你向它请求 String 的时候 , 它从来不会返回一个 Integer 给你 。 同时它也是异构的( heterogeneous ): 不像普通的映射,它的所有键都是不同类型的 。 因此,我们将 Favorites 称作类型安全的异构容器( typesafe heterogeneous container)。
Favorites 的实现小得出奇 。 它的完整实现如下:
public class Favorites {private Map<Class<?>, Object> favorites = new HashMap<>();public <T> void putFavorite(Class<T> type, T instance) {favorites.put (Objects.requireNonNull(type), instance);}public <T> T getFavorite(Class<T> type) {return type.cast(favorites.get(type)); }
}
这里面发生了一些微妙的事情 。 每个 Favorites 实例都得到一个称作 favorites 的私有 Map<Class<?>,Object >的支持 。 你可能认为由于无限制通配符类型的关系,将不能把任何东西放进这个 Map 中,但事实正好相反 。 耍注意的是通配符类型是嵌套的 : 它不是属于通配符类型的 Map 的类型,而是它的键的类型 。 由此可见,每个键都可以有一个不同的参数化类型:一个可以是 Class<String >,接下来是 Class<Integer >等 。 异构就是从这里来的 。
第二件要注意的事情是,favorites Map 的值类型只是 Object 。 换句话说,Map 并不能保证键和值之间的类型关系,即不能保证每个值都为它的健所表示的类型(通俗地说,就是指键与值的类型并不相同一一译者注) 。 事实上,Java 的类型系统还没有强大到足以表达这一点 。 但我们知道这是事实,并在获取 favorite 的时候利用了这一点 。
putFavorite 方法的实现很简单:它只是把(从指定的 Class 对象到指定的 favorite 实例) 一个映射放到 favorites 中 。 如前所述 ,这是放弃了键和值之间的“类型联系 ” ,因此无法知道这个值是键的一个实例 。 但是没关系,因为 getFavorites 方法能够并且的确重新建立了这种联系 。
getFavorite 方法 的 实 现比 pu tFavorite 的更难一些 。 它先从 favorites 映射中获得与指定 Class 对象相对应的值 。 这正是要返回的对象引用,但它的编译时类型是错误的 。 它的类型只是 Object (favorites 映射的值类型),我们需要返回一个 T 。因此,getFavorite 方法的实现利用 Class 的 cast 方法,将对象引用动态地转换( dynamicallycast )成了 Cl ass 对象所表示自由类型。
cast 方法是 Jav a 的转换操作符的动态模拟 。 它只检验它的参数是否为 Class 对象所表示的类型的实例 。 如果是,就返回参数;否则就抛出 ClassCastException 异常 。 我们知 道 getFavorite 中的 cast 调用永远不会抛出 ClassCastException 异常,并假设客户端代码正确无误地进行了编译 。 也就是说,我们知道 favor 工 tes 映射 中的值会始终与键的类型相匹配 。
假设 cast 方法只返回它的参数,那它能为我们做什么呢?cast 方法的签名充分利用了 Class 类被泛型化的这个事实 。 它的返回类型是 Class 对象的类型参数 :

这正是 getFavorite 方法所需要的,也正是让我们不必借助于未受检地转换成 T 就能确保 Favorites 类型安全的东西 。
总而言之,集合 API 说明了泛型的一般用法,限制 每个容器只 能有固定数目的类型参数 。 你可以通过将类型参数放在键上而不是容器上来避开这一限制 。 对于这种类型安全的异构容器,可以用 Class 对象作为键 。 以这种方式使用的 Class 对象称作类型令牌 。 你也可以使用定制的键类型 。 例如,用一个 DatabaseRow 类型表示一个数据库行(容器),用泛型 Column<T>作为它的键 。
相关文章:
Effective Java笔记(33)优先考虑类型安全的异构容器
泛型最常用于集合,如 Set<E >和 Map<K ,V>,以及单个元素的容器 ,如 ThreadLocal<T>和 AtomicReference<T> 。 在所有这些用法中,它都充当被参数化了的容器 。 这样就限制每个容器…...
释放AI创作潜能:从大模型训练到高产力应用
文章目录 每日一句正能量前言什么是人工智能生成内容(AIGC)人工智能生成内容(AIGC)能做什么为什么要用人工智能生成内容(AIGC)创作成果用Java实现冒泡排序算法学生信息收集系统学生请假管理系统需求分析教务…...
Ajax 笔记(一)—— Ajax 入门
笔记目录 1. Ajax 入门1.1 Ajax 概念1.2 axios 使用1.2.1 URL1.2.2 URL 查询参数1.2.3 小案例-查询地区列表1.2.4 常用请求方法和数据提交1.2.5 错误处理 1.3 HTTP 协议1.3.1 请求报文1.3.2 响应报文 1.4 接口文档1.5 案例1.5.1 用户登录(主要业务)1.5.2…...
Android Studio跳过Haxm打开模拟器
由于公司权限限制无法安装Haxm,这个时候我们可以试试Arm相关的镜像去跳过Haxm运行模拟器。解决方案:安装API27以下的Arm Image. #ifdef __x86_64__if (sarch "arm64" && apiLevel >28) {APANIC("Avds CPU Architecture %s i…...
从一个GPU到多个GPU
在多GPU运行应用程序时,需要正确设计GPU之间的通信,GPU间数据传输的效率取决于GPU是如何连接在一个节点上并跨集群的 在多GPU系统里有两种连接方式 多GPU通过单个节点连接到PCIe总线上 多GPU连接到集群中的网络交换机上 /* * 本示例演示了如何使用 Open…...
小白编写一个Chrome
步骤 1:了解插件的基本结构和功能 首先,向小白解释什么是Chrome插件,它是如何工作的,以及它可以做什么。强调插件可以修改网页内容、添加功能等。 步骤 2:准备工作 安装Chrome浏览器:确保小白的计算机上…...
自然语言处理学习笔记(六)————字典树
目录 1.字典树 (1)为什么引入字典树 (2)字典树定义 (3)字典树的节点实现 (4)字典树的增删改查 DFA(确定有穷自动机) (5)优化 1.…...
WPF实战项目十一(API篇):待办事项功能api接口
1、新建ToDoController.cs继承基础控制器BaseApiController,但是一般业务代码不写在控制器内,业务代码写在Service,先新建统一返回值格式ApiResponse.cs: public class ApiResponse{public ApiResponse(bool status, string mess…...
ffmpeg给视频添加时间水印,准确且不模糊
ffmpeg -i {输入文件路径} -vf{drawtext} {输出文件路径} 针对视频模糊,加上 -b:v {输出视频码率};右键属性,可查看离线视频源码率; 针对离线视频文件加上时间水印,时间跳变不正常,加上-re; 整…...
① vue复习。从安装到使用
vue官网:cn.vuejs.org vue安装 cnpm install -g vue/cli 查看是否安装成功 vue --version 创建一个项目 vue create vue-demo(项目名称) 这个取消掉。空格可选中或者取消。 运行项目: cd 进入到项目下 npm run serve 运行成功后,访问这…...
【Linux】多线程——线程引入 | 线程控制
文章目录 一、Linux多线程1. 线程概念2. 线程创建3. 线程和进程4. 线程的优缺点 二、线程控制1. 线程创建2. 线程终止3. 线程等待4. 线程分离5. 线程局部存储 三、线程封装 一、Linux多线程 一级页表和二级页表都是key/val模型,一级页表的key是第一份的10个比特位&a…...
查询树形目录(内存遍历成树返回)
实体 Data TableName("dtp_sm_servicetype") ApiModel(value "SmServicetype对象", description "服务类型") EqualsAndHashCode(callSuper true) public class SmServicetype extends BaseEntity {ApiModelProperty("服务类型名称&quo…...
Easys Excel的表格导入(读)导出(写)-----java
一,EasyExcel官网: 可以学习一些新知识: EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 二,为什么要使用easyexcle excel的一些优点和缺点 java解析excel的框架有很多 : poi jxl,存在问题:非常的消耗内存, easyexcel 我们…...
纯净版ISO镜像下载大全(Windows、Linux、mac)
目录 一、前言介绍 前言必读 介绍 二、获取ISO镜像方式 (一)官方镜像下载 (二)获取下载方式 ps:回复的内容都是小写的 Windows操作系统 1.windows XP系统 2.Windows 7系统 3.Windows10系统 4.Windows11系…...
VMware上的Centos设置静态IP
服务器环境一般都是Centos7,而且很多软件在Linux环境上也能支持得更好,所以我需要在本机上使用虚拟机安装Linux,因为需要访问Linux上安装的软件,所以需要固定IP,不然每次更改也不方便。 基础环境准备 安装VMware在VM…...
【MySQL】数据库的基本操作
文章目录 1. 创建数据库1.1 创建数据库的语句1.2 创建一个数据库1.3 查看字符串与校验规则1.4 校验规则对数据库的影响 2. 删除数据库3. 查看数据库4. 修改数据库5. 备份与恢复5.1 数据库的备份与恢复5.2 表的备份与恢复 6. 查看数据库的连接情况 1. 创建数据库 1.1 创建数据库…...
Spring整合MyBatis(详细步骤)
Spring与Mybatis的整合,大体需要做两件事, 第一件事是:Spring要管理MyBatis中的SqlSessionFactory 第二件事是:Spring要管理Mapper接口的扫描 具体的步骤为: 步骤1:项目中导入整合需要的jar包 <dependency><!--Spring操作数据库需要该jar包…...
Linux:Shell编程之正则表达式
目录 绪论 1、正则表达式 1.1 通配符 1.2 正则表达式分类 1.3 基本正则 1.4 正则表达式中表示次数的表达式 1.5 位置锚定 1.5.1 词首锚定和词尾锚定 1.6 分组() 1.7 逻辑或 1.8 扩展正则 绪论 正则表达式:有一类特殊字符以及文本…...
Python Opencv实践 - 图像缩放
import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg_cat cv.imread("../SampleImages/cat.jpg", cv.IMREAD_COLOR) plt.imshow(img_cat[:,:,::-1])#图像绝对尺寸缩放 #cv.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) #指定Size大…...
大脑营行|“福安市华龙教育基金”支持家乡教育事业发展
8月8日,福安市松罗中学举行“福安市华龙教育基金”中考奖学金颁发仪式。福安市松罗乡党委书记钟文、乡长郑仁寿、福安市人民政府教育督导室副科级督导员(片区领导)陈秦、校长张明亮、各村支部书记、家长代表、受奖学生,校领导班子…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑 在电子商务领域,转化率与网站性能是决定商业成败的核心指标。今天,我们将深入解析不同类型电商平台的转化率基准,探讨页面加载速度对用户行为的…...
