Effective Java笔记(26)请不要使用原生态类型

        首先介绍一些术语 。 声明中具有一个或者多个类型参数( type parameter )的类或者接口,就是泛型( generic )类或者接口 。 例如,List 接口就只有单个类型参数 E ,表示列表的元素类型 。这个接口的全称是 List<E> (读作“ E 的列表”),但是人们经常把它简称为 List 。 泛型类和接口统称为泛型( generic type ) 。

        每一种泛型定义一组参数化的类型( parameterized type),构成格式为 : 先是类或者接口的名称,接着用尖括 号(<>) 把对应于泛型形式类型参数的实际类型参数列表括起来 。 例如,List< String> (读作 “字符串列表”)是一个参数化的类型,表示元素类型为 String 的列表 。(String 是与形式的类型参数 E 相对应的实际类型参数 。)

        最后一点,每一种泛型都定义一个原生态类型( raw type ),即不带任何实际类型参数的泛型名称 。 例如,与 List < E>相对应的原生态类型是 List 。 原生态类型就像从类型声明中删除了所有泛型信息一样 。 它们的存在主要是为了与泛型出现之前的代码相兼容 。

        在 Java 增加泛型之前,下面这个集合声明是值得参考的 。 从 Java 9 开始,它依然合法,但是已经没什么参考价值了:

private final Collection stamps = ...;

        如果现在使用这条声明,并且不小心将一个 coin 放进了 stamp 集合中,这一错误的插入照样得以编译和运行,不会出错(不过编译器确实会发出一条模糊 的警告信息) :

stamps.add(new Coin( ... ));

直到从 stamp 集合中获取 co 工 n 时才会收到一条错误提示:

for (Iterator i = stamps.iterator(); i.hasNext(); )Stamp stamp = (Stamp) i.next(); stamp.cancel() ;

        出错之后应该尽快发现, 最好是编译时就发现。在本例中,直到运行时才发现错误,已经出错很久了,而且它在代码中所处的位置,距离包含错误的这部分代码已经很远了 。一旦发现ClassCastException ,就必须搜索代码,查找将coin 放进 stamp 集合的方法调用 。 此时编译器帮不上忙,因为它无法理解这种注释 :“ Contains only Stamp instances ”(只包含 Stamp 实例)。

有了泛型之后,类型声 明中可以包含以下信息,而不是注释 :

private final Collection<Stamp> stamps = ...;

        通过这条声明,编译器知道 stamps 应该只包含 Stamp 实例,并给予保证( guarantee),假设整个代码库在编译过程中都没有发出任何警告 。当 stamps利用一个参数化的类型进行声明时,错误的插入会产生一条编译时的错误消息,告诉你具体是哪里出错了。

        从集合中检索元素时,编译器会替你插入隐式的转换,并确保它们不会失败(依然假设所有代码都没有产生或者隐瞒任何编译警告) 。 假设不小心将 coin 插入 stamp 集合,这显得有点牵强,但这类问题却是真实的 。 例如,很容易想象有人会不小心将一个Biginteger 实例放进一个原本只包含 BigDecimal 实例的集合中 。

        如上所述,使用原生态类型(没有类型参数的泛型)是合法的,但是永远不应该这么做 。 如果使用原生态类型,就失掉了泛型在安全性和描述性方面的所有优势 。 既然不应该使用原生态类型 ,为什么 Java 语言的设计者还要允许使用它们呢?这是为了提供兼容性 。

        因为泛型出现的时候,Java 平台即将进入它的第二个十年,已经存在大量没有使用泛型的Java 代码 。 人们认为让所有这些代码保持合法,并且能够与使用泛型的新代码互用,这一点很重要。它必须合法才能将参数化类型的实例传递给那些被设计成使用普通类型的方法,反之亦然 。 这种需求被称作移植兼容性( Migration Compatibility ),促成了支持原生态类型,以及利用擦除( erasure) 实现泛型的决定 。

        虽然不应该在新代码中使用像 List 这样的原生态类型,使用参数化的类型以允许插入任意对象(比如 List<Object >)是可行的 。 原生态类型 List 和参数化的类型 List<Object>之间到底有什么区别呢?不严格地说,前者逃避了泛型检查,后者则明确告知编译器,它能够持有任意类型的对象。 虽然可以将 List<String> 传递给类型 List 的参数,但是不能将它传给类型 List<Object>的参数。 泛型有子类型化( subtyping )的规则,List<String>是原生态类型 List 的一个子类型,而不是参数化类型 List<Object>的子类型 。因此, 如果使用像 List 这样的原生态类型,就会失掉类型安全性 , 但是如果使用像 List<Object >这样的参数化类型,则不会

        为了更具体地进行说明,请参考下面的程序:

public static void main(String[] args) {List<String> strings = new ArrayList<>();unsafeAdd(strings, Integer.value0f(42));String s = strings.get(0); // Has compiler-generated castprivate static void unsafeAdd(List list, object o) {list.add(o);
}

        这段程序可以进行编译,但是因为它使用了原生态类型 List ,你会收到一条警告:

        实际上,如果运行这段程序,在程序试图将 strings.get(o)的调用结果 Integer转换成 String时,你会收到一个 ClassCastException 异常 。 这是一个编译器生成的转换,因此一般保证会成功,但是我们在这个例子中忽略了一条编译器警告,为此付出了代价 。如果在 unsafeAdd 声明中用参数化类型 List<Object>代替原生态类型 List ,并试着重新编译这段程序,会发现它无法再进行编译了,并发出以下错误消息:

        在不确定或者不在乎集合中的元素类型的情况下,你也许会使用原生态类型 。 例如,假设想要编写一个方法,它有两个集合,并从中返回它们共有元素的数量 。 如果你对泛型还不熟悉,可以参考以下方式来编写这种方法:

static int numElementsInCommon(Set s1, Set s2) {int result = 0;for (Object o1 : s1)if (s2.contains(o1))result++;return result;
}

        这个方法可行,但它使用了原生态类型,这是很危险的 。 安全的替代做法是使用无限制的通配符类型( unbounded wildcard type ) 。 如果要使用泛型,但不确定或者不关心实际的类型参数,就可以用一个问号代替 。 例如,泛型 Set<E>的无限制通配符类型为 Set <?>(读作“某个类型的集合”) 。 这是最普通的参数化 S et 类型,可 以持有任何集合 。 下面是numElementsinCommon 方法使用了无限制通配符类型时的情形 :

static int numElementsInCommon(Set<?> s1, Set<?> s2) {...}

        无限制通配类型 Set <>和原生态类型 Set 之间有什么区别呢?这个问号真正起到作用了吗?这一点不需要赘述,但通配符类型是安全的,原生态类型则不安全 。 由于可以将任何元素放进使用原生态类型的集合中,因此很容易破坏该集合的类型约束条件(如之前范例中所示的 unsafeAdd 方法);但不能将任何元素(除了 null 之 外)放到 Collection < ?>中 。 如果尝试这么做,将会产生一条像这样的编译时错误消息:

        这样的错误消息显然无法令人满意,但是编译器已经尽到了它的职责,防止你破坏集合的类型约束条件 。 你不仅无法将任何元素(除了 null 之外)放进 Collection <?> 中,而且根本无法猜测你会得到哪种类型的对象 。 要是无法接受这些限制,就可以使用泛型方法或者有限制的通配符类型 。

        不要使用原生态类型,这条规则有几个小小的例外 。 必须在类文字中 使用原生态类型 。 规范不允许使用参数化类型(虽然允许数组类型和基本类型)。换句话说, List.class 、 String[].class 和 int.class 都合法,但是 List<String.class 和 List<?>.class 则不合法 。

        这条规则的第二个例外与 ins ta口 ceof 操作符有关 。 由于泛型信息可以在运行时被擦除,因此在参数化类型而非无限制通配柯:类型上使用 instanceof 操作符是非法的 。 用无限制通配符类型代替原生态类型,对 instanceof 操作符的行为不会产生任何影响 。 在这种情况下,尖括号( <>)和问号(?)就显得多余了 。 下面是利用泛型来使用 instanceof操作符的首选方法

if (o instanceof Set) {Set<?> s = (Set<?>) o;
}

        注意,一旦确定这个 o 是个 Set ,就必须将它转换成通配符类型 Set <?>,而不是转换成原生态类型 Set 。 这是个受检的( checked )转换,因此不会导致编译时警告 。

        总而言之,使用原生态类型会在运行时导致异常,因此不要使用 。 原生态类型只是为了与引人泛型之前的遗留代码进行兼容和互用而提供的 。 让我们做个快速的回顾:Set<Object >是个参数化类型,表示可以包含任何对象类型的一个集合;Set <?>则是一个通配符类型,表示只能包含某种未知对象类型的一个集合;Set 是一个原生态类型,它脱离了泛型系统 。 前两种是安全的,最后一种不安全。

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

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

相关文章

竞赛项目 深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

freeswitch的mod_xml_curl模块动态获取dialplan

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 mod_xml_curl模块支持从web服务获取xml配置&#xff0c;本文介绍如何动态获取dialplan配置。 环境 centos&#xff1a;CentOS release 7.0 (Final)或以上版本 freeswitch&#xff1a;v1.6.20 GCC&#xff1a;4.8.5…

【HarmonyOS】Java如何引用外部jar包

【关键字】 Java、引用jar包​ 【写在前面】 使用API6和API7开发HarmonyOS应用时&#xff0c;因为应用中只能引用SDK中开放的功能接口&#xff0c;但是部分jdk自带的接口功能在SDK中并未封装&#xff0c;要想在工程中使用jdk开放的接口功能&#xff0c;需要将jdk中的jar包通过…

科技资讯|苹果手机版Vision Pro头显专利曝光,内嵌苹果手机使用

根据美国商标和专利局&#xff08;USPTO&#xff09;公示的清单&#xff0c;苹果公司近日获得了一项头显相关的技术专利&#xff0c;展示了一款亲民款 Vision Pro 头显&#xff0c;可以将 iPhone 放置在头显内部充当屏幕。 根据patentlyapple 媒体报道&#xff0c;这是苹果公司…

1.RuoYi-Vue前后端分离版本启动

1.代码下载 去若依官网&#xff0c;选择RuoYI前后端分离版: 下载地址&#xff1a;https://gitee.com/y_project/RuoYi-Vue 2.依赖环境的部署 1.Mysql 2.Redis安装部署 : https://blog.csdn.net/qq_27860623/article/details/132168382 3.Idea打开后端服务 用Idea打开后端的…

计算机服务器被360后缀勒索病毒攻击怎么办,勒索病毒解密

计算机技术的不断发展&#xff0c;不仅方便了企业的生产生活&#xff0c;也为社会的发展带来了巨大贡献&#xff0c;但随之而来的网络威胁也不断增加&#xff0c;勒索病毒就是其中较为常见的常见的威胁。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的计算机服务器…

Qt应用开发(基础篇)——堆栈窗口 QStackedWidget

一、前言 QStackedWidget继承于QFrame&#xff0c;QFrame继承于QWidget&#xff0c;是Qt常用的堆栈窗口部件。 框架类QFrame介绍 QStackedWidget堆栈窗口&#xff0c;根据下标切换&#xff0c;一次显示一个小部件&#xff0c;常用于应用界面切换、图片轮询播放等场景。 二、QSt…

Unity-Shader-高亮Highlight

常用Shader-高亮&#xff0c;可动态调整高亮颜色、高亮强度范围/等级、高亮闪烁速度、高亮状态 Shader "CustomShader/Highlight" {Properties{_Color("Color", Color) (0.9044118,0.6640914,0.03325041,0)_Albedo("Albedo", 2D) "white…

Stable Diffusion - Candy Land (糖果世界) LoRA 提示词配置与效果展示

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132145248 糖果世界 (Candy Land) 是一个充满甜蜜和奇幻的地方&#xff0c;由各种各样的糖果和巧克力构成。在糖果世界&#xff0c;可以看到&…

全网最细,Fiddler修改接口返回数据详细步骤实战,辅助接口测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在测试的过程中&a…

年之年的选择,组装版

组件&#xff1a;<!--* Author: liuyu liuyuxizhengtech.com* Date: 2023-02-01 16:57:27* LastEditors: wangping wangpingxizhengtech.com* LastEditTime: 2023-06-30 17:25:14* Description: 时间选择年 - 年 --> <template><div class"year-range-pick…

Nacos服务治理—负载均衡

引入负载均衡 在消费方引入负载均衡机制&#xff0c;同时简化获取服务提供者信息的流程 Spring Cloud引入组件LoadBalance实现负载均衡 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web<…

MyBatis-动态SQL-foreach

目录 标签有以下常用属性&#xff1a; 小结 <froeach> <foreach>标签有以下常用属性&#xff1a; collection&#xff1a;指定要迭代的集合或数组的参数名&#xff08;遍历的对象&#xff09;。item&#xff1a;指定在迭代过程中的每个元素的别名&#xff08;遍历…

【python 深度学习】解决遇到的问题

目录 一、RuntimeError: module compiled against API version 0xc but this version of numpy is 0xb 二、AttributeError: module ‘tensorflow’ has no attribute ‘flags’ 三、conda 更新 Please update conda by running 四、to search for alternate channels that…

【Spring专题】手写简易Spring容器过程分析

前置知识 《【Spring专题】Spring底层核心原理解析》 思路整理 我们在上一节《【Spring专题】Spring底层核心原理解析》课里面有简单分析过一个Spring容器的一般流程&#xff0c;所以&#xff0c;本节课我们这里尝试写一下简易的Spring容器。 手写源码示例 一、手写前的准…

[足式机器人]Part4 机械设计 Ch00/01 绪论+机器结构组成与连接 ——【课程笔记】

本文仅供学习使用 本文参考&#xff1a; 《机械设计》 王德伦 马雅丽课件与日常作业可登录网址 http://edu.bell-lab.com/manage/#/login&#xff0c;选择观摩登录&#xff0c;查看2023机械设计2。 机械设计-Ch00Ch01——绪论机器结构组成与连接 Ch00-绪论0.1 何为机械设计——…

python爬虫实战(1)--爬取新闻数据

想要每天看到新闻数据又不想占用太多时间去整理&#xff0c;萌生自己抓取新闻网站的想法。 1. 准备工作 使用python语言可以快速实现&#xff0c;调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义请求头&#xf…

Servlet介绍

1.简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一&#xff0c;其实就是一个接口&#xff0c;将来我们需要定义Servlet类…

【果树农药喷洒机器人】Part3:变量喷药系统工作原理介绍

本专栏介绍&#xff1a;免费专栏&#xff0c;持续更新机器人实战项目&#xff0c;欢迎各位订阅关注。 关注我&#xff0c;带你了解更多关于机器人、嵌入式、人工智能等方面的优质文章&#xff01; 文章目录 一、变量喷药系统工作原理二、液压通路设计与控制系统封装2.1液压通路…

OSPF技术入门(第三十四课)

1 OSPF的介绍 OSPF是一种链路状态路由协议,主要用于IP网络中的路由选择。它是一种开放协议,能够在不同的网络设备之间进行通信。OSPF利用链路状态数据库来描述网络拓扑结构,并通过Dijkstra算法计算出最短路径。它支持按照精确度划分的路由优先级,以及多个相等的路径,并能自…