Effective Java笔记(29)优先考虑泛型

        一般来说 ,将集合声 明参数化,以及使用 JDK 所提供的泛型方法,这些都不太困难 。编写自己的泛型会比较困难一些,但是值得花些时间去学习如何编写 。

        以简单的(玩具)堆校实现为例 :

// Object -based collection - a prime candidate for generics
public class Stack
{private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() {elements = new Object[DEFAULT_INITIAL_CAPACITY];}public void push(0bject e) {ensureCapacity();elements[size++] = e;}public object pop() {if (size == 0)throw new EmptyStackException();Object result = elements[--size];elements[size] = null; // Eliminate obsolete referencereturn result;}public boolean isEmpty () {return size == 0;}private void ensureCapacity () {if (elements.length == size)elements = Arrays.copyOf(elements, 2 * size + 1);}
}

        这个类应该先被参数化,但是它没有,我们可以在后面将它泛型化( generify ) 。 换句话说,可以将它参数化,而又不破坏原来非参数化版本的客户端代码 。 也就是说,客户端必须转换从堆楼里弹出的对象,以及可能在运行时失败的那些转换 。 将类泛型化的第一步是在它的声明中添加一个或者多个类型参数 。 在这个例子中有一个类型参数,它表示堆桔的元素类型,这个参数的名称通常为 E 。

        下一步是用相应的类型参数替换所有的 Object 类型,然后试着编译最终的程序:

public class Stack<E> {private E[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() {elements = new E[DEFAULT_INITIAL_CAPACITY] ;}public void push(E e) {ensureCapacity();elements[size++] = e;}public E pop( {if (size == 0)throw new EmptyStackException();E result = elements[--size];elements[size] = null; // Eliminate obsolete referencereturn result;}...// no changes in isEmpty or ensureCapacity
}

        通常,你将至少得到 一个错误提示或警告,这个类也不例外 。 幸运的是,这个类只产生一个错误,内容如下:

        你不能创建不可具体化的( non-reifiable )类型的数组,如 E 。 每当编写用数组支持的泛型时,都会出现这个问题 。 解决这个问题有两种方法 。 第一种,直接绕过创建泛型数组的禁令 : 创建一个 Object 的数组,并将它转换成泛型数组类型 。 现在错误是消除了,但是编译器会产生一条警告 。 这种用法是合法的,但(整体上而言)不是类型安全的:

        编译器不可能证明你的程序是类型安全的,但是你可以 。 你自己必须确保未受检的转换不会危及程序的类型安全性 。 相关的数组(即 elements 变量)保存在一个私有的域中,永远不会被返回到客户端,或者传给任何其他方法 。 这个数组中保存的唯一元素,是传给push 方法的那些元素,它们的类型为 E ,因此未受检的转换不会有任何危害 。 

        一旦你证明了未受检的转换是安全的,就要在尽可能小的 范围中禁止警告 。 在这种情况下,构造器只包含未受检的数组创建,因此可以在整个构造器中禁止这条警告 。 通过增加一条注解   @SuppressWarnings 来完成禁止,Stack 能够正确无误地进行编译,你就可以使用它了,无须显式的转换,也无须担心会出现 ClassCastException 异常:

@SuppressWarnings ("unchecked")
public Stack() {elements = (E[]) new Object [DEFAULT_INITIAL_CAPACITY];
}

         消除 Stack 中泛型数组创建错误的第 二种方法是,将 elements 域的类型从 E []改为 Object[] 。 这么做会得到一条不同的错误:

         通过把从数组中获取到的元素由 Object 转换成 E ,可以将这条错误变成一条警告:

        由于 E 是一个不可具体化的( non -reifiable )类型,编译器无法在运行时检验转换 。 你还是可以自己证实未受检的转换是安全的,因此可以禁止该警告 。 我们只要在包含未受检转换的任务上禁止警告,而不是在整个 pop 方法上禁止就可以了,方法如下:

public E pop() {if(size==0)throw new EmptyStackException();// push requires elements to be of type E, so cast is correct@SuppressWarnings ("unchecked") E result =(E) elements[--size];elements[size] = null; // Eliminate obsolete referencereturn result;
}

        这两种消除泛型数组创建的方法,各有所长 。 第一种方法的可读性更强 : 数组被声明为 E []类型清楚地表明它只包含 E 实例 。 它也更加简洁 : 在一个典型的泛型类 中,可以 在代码中的多个地方读取到该数组;第一种方法只需要转换一次(创建数组的时候),而第二种方法则是每次读取一个数组元素时都需要转换一次 。 因此,第一种方法优先,在实践中也更常用 。 但是,它会导致堆污染( heap pollution ),详见第 32 条 : 数组的运行时类型与它 的编译时类型不匹配(除非 E 正好是 Object ) 。 这使得有些程序员会觉得很不舒服,因而选择第二种方案,虽然堆污染在这种情况下并没有什么危害 。

        下面的程序示范了泛型 Stack 类的使用方法 。 程序以倒序的方式打印出它的命令行参数,并转换成大写字母 。 如果要在从堆战中弹出的元素上调用 String 的 toUpperCase方法,并不需要显式的转换,并且确保自动生成的转换会成功:

public static void main(String[] args) {Stack<String> stack = new Stack<>();for (String arg : args)stack.push(arg);while (!stack.isEmpty())System.out.println(stack.pop().toUpperCase();
}

        看来上述的示例与第 28 条相矛盾了,第 28 条鼓励优先使用列表而非数组 。 实际上不可能总是或者总想在泛型中使用列表 。Java 并不是生来就支持列表,因此有些泛型如 ArrayList,必须在数组上实现 。 为了提升性能,其他泛型如 HashMap 也在数组上实现 。

        总而言之,使用泛型比使用需要在客户端代码中进行转换的类型来得更加安全,也更加容易。 在设计新类型的时候,要确保它们不需要这种转换就可以使用 。 这通常意味着要把类做成是泛型的。 只要时间允许,就把现有的类型都泛型化 。 这对于这些类型的新用户来说会变得更加轻松,又不会破坏现有的客户端 。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

诚迈科技亮相华为开发者大会2023,打造万物互联全场景生态

8月4-6日&#xff0c;华为开发者大会2023在中国松山湖盛大举行&#xff0c;诚迈科技作为华为合作伙伴携一系列基于OpenHarmony和HarmonyOS Connect的创新技术及生态成果&#xff0c;精彩亮相OpenHarmony共建展区、OpenHarmony使能展区和鸿蒙智联展区&#xff0c;吸引了众多行业…

使用Python和wxPython构建中文OCR截图工具

引言&#xff1a; 随着数字化时代的到来&#xff0c;我们经常需要从图像中提取文本信息。而在处理中文文本时&#xff0c;OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;技术发挥着重要作用。本文将介绍如何使用Python编程语言和wxPython图…

记录线上一次mysql只能查询,不能插入或更新的bug

错误复现 突然有一天产品通知xx服务不可用&#xff0c;想着最近也没有服务更新&#xff0c;就先排查一下服务日志 使用postman测试的时候请求明显超时&#xff0c;查看日志显示是一个锁的问题 使用工具连接到mysql&#xff0c;查看information_schema.INNODB_TRX,发现有一个事…

CVE漏洞复现-CVE-2021-3493 Linux 提权内核漏洞

CVE-2021-3493 Linux 提权内核漏洞 漏洞描述 CVE-2021-3493 用户漏洞是 Linux 内核中没有文件系统中的 layfs 中的 Ubuntu over 特定问题&#xff0c;在 Ubuntu 中正确验证有关名称空间文件系统的应用程序。buntu 内核代码允许低权限用户在使用 unshare() 函数创建的用户命名…

【linux-keepalive】keepalive避免单点故障,高可用配置

keepalive: [rootproxy ~]# yum install -y keepalived [rootproxy ~]# vim /etc/keepalived/keepalived.conf global_defs {router_id proxy1 //设置路由ID号vrrp_iptables //不添加任何防火墙规则 } vrrp_instance V…

python的virtualenv虚拟环境无法激活activate

目录 问题描述&#xff1a; 解决办法&#xff1a; 解决结果&#xff1a; 问题描述&#xff1a; PS D:\pythonProject\pythonProject\DisplayToolLibs\venv\Scripts> .\activate .\activate : 无法加载文件 D:\pythonProject\pythonProject\DisplayToolLibs\venv\Scripts\…

sql注入漏洞

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 原因危害如何挖寻找注入点测试是否有绕过实质分类sqli靶场盲注时间盲注别人的总结UA注入referer注入DNSlog外带cookie注入宽字节注入堆叠注入sqlmap工具getshellacc…

CUDA版本自由切换

下载特定版本cuda 在英伟达官网下载相关驱动 https://developer.nvidia.com/cuda-toolkit-archive 然后点点点&#xff0c;选择runfile(local) 本地运行后先continue Do you accept the previously read EULA? accept/decline/quit: accept按回车取消驱动安装 gcc版本不…

MySQL 主从复制

MySQL主从复制是一种数据复制技术&#xff0c;用于将一个MySQL数据库的数据实时复制到其他MySQL数据库&#xff0c;通常一个作为主数据库&#xff08;master&#xff09;&#xff0c;其他作为从数据库&#xff08;slave&#xff09; 基本工作原理&#xff1a; 主数据库记录所有…

CSDN 直播:腾讯云大数据 ES 结合 AI 大模型与向量检索的新一代云端检索分析引擎 8月-8号 19:00-20:30

本次沙龙围绕腾讯云大数据ES产品展开&#xff0c;重点介绍了腾讯云ES自研的存算分离技术&#xff0c;以及能与AI大模型和文本搜索深度结合的高性能向量检索能力。同时&#xff0c;本次沙龙还将为我们全方位介绍腾讯云ES重磅推出的Elasticsearch Serverless服务&#xff0c;期待…

大模型开发(十六):从0到1构建一个高度自动化的AI项目开发流程(中)

全文共1w余字&#xff0c;预计阅读时间约40~60分钟 | 满满干货(附代码)&#xff0c;建议收藏&#xff01; 本文目标&#xff1a;通过LtM提示流程实现自动构建符合要求的函数&#xff0c;并通过实验逐步完整测试code_generate函数功能。 代码下载点这里 一、介绍 此篇文章为…

SQL必知必会读书笔记

文章目录 **不同的DB语法格式不尽相同**第一课 了解SQL术语 第二课 检索数据语法格式检索列检索唯一不同值限制结果&#xff08;数量&#xff09; 第三课 排序检索数据使用说明 第四课 过滤数据WHERE子句操作符 第五课 高级数据过滤1、组合WHERE子句2、IN操作符3、NOT操作符 第…