Effective Java笔记(33)优先考虑类型安全的异构容器

news2025/2/25 11:15:02

        泛型最常用于集合,如 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>作为它的键 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/863108.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于C函数封装的C++读写锁(附源码)

C++常用功能源码系列 文章目录 C++常用功能源码系列前言一、读写锁是什么二、读写锁封装源码三、读写锁注意点总结前言 本文是C/C++常用功能代码封装专栏的导航贴。部分来源于实战项目中的部分功能提炼,希望能够达到你在自己的项目中拿来就用的效果,这样更好的服务于工作实践…

Vue组件的嵌套关系;父组件传递子组件props;子组件传递给父组件$emit;自定义事件;案例

目录 1_Vue组件的嵌套关系1.1_认识组件的嵌套1.2_组件的拆分1.3_组件的通信 2_父组件传递子组件props2.1_父子组件之间通信的方式2.2_父组件传递给子组件2.3_Props的对象用法 3_子组件传递给父组件$emit4_自定义事件(了解)5_小案例6_补充 1_Vue组件的嵌套关系 1.1_认识组件的嵌…

java 11 新特效解读(2)

目录 全新的HTTP 客户端API 更简化的编译运行程序 废弃Nashorn引擎 ZGC 优势&#xff1a; ZGC的设计目标是&#xff1a; 在当前JDK中看不到什么&#xff1f; 一个标准化和轻量级的JSON API 新的货币API 展望 全新的HTTP 客户端API HTTP&#xff0c;用于传输网页的…

C++实现俄罗斯方块(源码+详解)

&#x1f442; Take me Hand Acoustic - Ccile Corbel - 单曲 - 网易云音乐 源码Debug工具 &#xff08;1&#xff09;cppreference.com &#xff08;主&#xff09; &#xff08;2&#xff09;必应 (bing.com) &#xff08;3&#xff09;GPT&#xff08;主&#xff09; &#…

Java多线程(3)---锁策略、CAS和JUC

目录 前言 一.锁策略 1.1乐观锁和悲观锁 ⭐ 两者的概念 ⭐实现方法 1.2读写锁 ⭐概念 ⭐实现方法 1.3重量级锁和轻量级锁 1.4自旋锁和挂起等待锁 ⭐概念 ⭐代码实现 1.5公平锁和非公平锁 1.6可重入锁和不可重入锁 二.CAS 2.1为什么需要CAS 2.2CAS是什么 ⭐CAS…

那些年的golang开发经验记录

goland 问题CreateProcess error216, 该版本的 %1 与你运行的 Windows 版本不兼容。请查看计算机的系统信息&#xff0c;然后联系软件发布者 Cannot run program "......" (in directory "D:\project\go\awesomeProject\src\test"): CreateProcess error2…

c语言内存管理

1、局部变量&静态局部变量 1、局部变量 a、作用域&#xff1a;在定义变量的{}之内有效 b、声明周期&#xff1a;程序运行至变量定义处开辟内存空间&#xff0c;所在函数运行结束之后释放空间 c、未初始化的变量值&#xff1a;随机 2、静态局部变量 a、作用域&#xff1…

AI人工智能简史

最近学习AI&#xff0c;顺便整理了一份AI人工智能简史&#xff0c;大家参考&#xff1a; 1951年 第一台神经网络机&#xff0c;称为SNARC&#xff1b;1956年 达特茅斯学院会议&#xff0c;正式确立了人工智能的研究领域&#xff1b;1966年 MIT发明ELIZA人机心理治疗对话程序&a…

Python测试框架pytest:常用参数、查找子集、参数化、跳过

Pytest是一个基于python的测试框架&#xff0c;用于编写和执行测试代码。pytest主要用于API测试&#xff0c;可以编写代码来测试API、数据库、UI等。 pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有以下几个优点&#xff1a; 简单灵活&#xff0c;容易上手。…

检测ChatGPT生成内容的工具经常误报,怪不得OpenAI停用它

​近日&#xff0c;加利福尼亚大学戴维斯分校的学生被指控使用ChaGPT作弊。他的老师指控他在历史考试中使用了ChatGPT&#xff0c;这一指控得到了某生成式内容检测工具的支持。然而&#xff0c;该名学生坚决否认这一指控&#xff0c;他通过提供在线文档的编辑日志为自己洗清了嫌…

时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比

时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比 目录 时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 1.MATLAB实现EEMD-GRU、GRU时…

vue3多页面配置你一定会遇到的问题,踩坑指南

vue3实现多页面打包容易&#xff0c;关键是如何实现本地的开发和调试&#xff1f;我们接下来解决如下几个问题&#xff1a; 1 多页面项目的项目结构是怎样的&#xff1f; --public--src---App.vue---main.js---page1. ---App.vue---main.js----home.vue----list.vue---page2.…

Hadoop集群部署-完全分布式

文章目录 一、概述二、架构三、部署1. 基础环境配置2. 创建hadoop用户并且生成密钥3. 配置三台服务器免密登录4. Zookeeper安装5. JDK与Hadoop安装6. 配置环境变量7. 启动Zookeeper8. 配置HDFS9. 启动journalnode10. master节点格式化11. 配置YARN12. Hadoop开启Histotryserver…

无涯教程-Perl - lc函数

描述 此函数返回小写版本的EXPR,如果省略EXPR,则返回$_。 语法 以下是此函数的简单语法- lc EXPRlc返回值 此函数返回小写版本的EXPR,如果省略EXPR,则返回$_。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl$orig_string"This is Test and CAPITAL"; …

腾讯云服务器租用价格表_轻量_CVM_GPU(2023更新)

腾讯云服务器租用费用表新鲜出炉2023年更新&#xff0c;轻量应用服务器2核2G4M带宽112元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、云服务器CVM S5实例2核2G配置280.8元一年、GPU服务器GN10Xp实例145元7天&#xff0c;腾讯云服务器网…

黑马程序员Vue全套视频

Vue 2 创建vue实例 示例: <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script src"https://cdn.jsdelivr.net/npm/vue2.…

全网最全ChatGPT/AIGC报告分析(365份)

本星球整理了365份ChatGPT/AIGC重磅专业报告&#xff08;更新中…&#xff09;&#xff0c;部分目录如下&#xff0c;请读者搜索关键字&#xff0c;获取报告&#xff0c;学习参阅。 https://mp.weixin.qq.com/s/hvI2Hupjx_mnPh3YGyobww 1、计算机研究报告&#xff1a;ChatCPT…

vscode里面报:‘xxx‘ is assigned a value but never used.解决办法

const setCurPage: React.Dispatch<React.SetStateAction<number>> 已声明“setCurPage”&#xff0c;但从未读取其值。ts(6133) setCurPage is assigned a value but never used.eslinttypescript-eslint/no-unused-vars 出现这个报错是eslint导致的&#xff0…

竞赛项目 车位识别车道线检测 - python opencv

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 车位识别车道线检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) …

基于Containerd容器引擎和kubeadm工具部署K8sv1.26.3

kubernetes master节点及node节点各核心组件的功能概述 master节点主要组件概述 master节点主要是k8s的控制节点&#xff0c;在master节点上主要有三个组件必不可少&#xff0c;apiserver、scheduler和controllermanager&#xff1b;etcd是集群的数据库&#xff0c;是非常核心的…