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

《Effective Java》第26条:请不要使用原生态类型 为什么是不合法的,原因有二,一是泛型的「类型擦除」机制 ,Java 的泛型是编译期特性,运行时会执行「类型擦除」—— 即参数化类型的泛型信息会被擦除,还原为原生态类型,也就是说,编译期的 List 和 List,在运行时都会被擦除为 List(原生态类型)。在日常的编程中,在不确定或者不在乎集合中的元素类型的情况下,你也许会使用原生态类型,例如书上P96页的例子,但这也不是一种好的方式使用原生态类型,此时通常会使用一种写法:Set set = new HashSet();这是为了提供兼容性。 阅读详情

        首先介绍一些术语 。 声明中具有一个或者多个类型参数( 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 cast

private 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 是一个原生态类型,它脱离了泛型系统 。 前两种是安全的,最后一种不安全。

2.4G板载PCB IFA天线仿真设计 本文简单介绍下2.4GHz的板载PCB IFA天线的HFSS仿真与设计。 阅读详情

相关推荐

本地大模型部署实战:LM Studio与Ollama的易用性与性能深度评测

本文对两款主流本地大模型部署工具LM Studio和Ollama进行了深度评测。LM Studio凭借图形化界面和内置模型库,为新手提供了开箱即用的便捷体验,并支持OpenAI兼容API,易于生态集成。Ollama则以命令行驱动,轻量高效,凭借其原生API和Modelfile自定义能力,在自动化集成和性能方面表现突出。文章从易用性、模型生态、性能及扩展性多维度对比,帮助用户根据自身技术背景和使用场景做出最佳选择。

weixin_29039773的博客 528

Java中的原生数据类型

Java中的原生数据类型(Primitive DataType)共有8种: 1)整型: 使用int表示(32位)。2)字节型:使用byte表示(从-128到127之间的256个整数)。3)短整型:使用short表示(16位)。4)长整型:使用long表示(64位)。5)单精度浮点型:使用float表示。所谓浮点型,指的是小数,也叫实数,例如:1.2。6)双精度浮点型:使用doubl...

weixin_30485799的博客 236

【Cadence 17.4实战】----- 基于MySQL与ODBC的企业级CIS数据库配置全攻略

本文详细介绍了如何在Cadence 17.4中配置基于MySQL与ODBC的企业级CIS数据库,提升硬件设计团队的元器件管理效率。通过实战案例和避坑指南,帮助读者掌握数据库部署、ODBC驱动配置及CIS参数映射等关键技巧,实现设计数据的实时同步与高效管理。

weixin_30591551的博客 327

java 泛型之不要使用原生态类型

从java1.5发行版本中增加了泛型(Generic)。在没有泛型之前,从集合中读取到的每一个对象都是必须进行转换的。如果不小心插入了类型错误的对象,在运行时的转换处理就会出错。有了泛型以后,可以告诉编译器每个集合中接收哪些对象类型。编译器就会自动地为你的插入进行转化,并在编译时告知是否插入了类型错误的对象。这样可以是程序既更加安全,又更加清楚,但是要享用这些优势有一定的难度。原生态类型和无限制的通配符概念

蘑菇蘑菇不会开花的专栏 2880

【Unity报错】Unity导入海康SDK 报错 DllNotFoundException: PlayCtrl assembly

2.尝试按网上其他的办法(包括使用depengs查看dll依赖等)1.先检查Plugins文件夹中是否存在PlayCtrl文件。3.如果网上查阅的办法都不能解决,请继续往下看。更换低版本的SDK库文件。

weixin_42968584的博客 1126

缺陷检测:使用PatchCore训练自己的数据集

必须包含有训练图片(无缺陷图片)、测试图片(缺陷图片)和ground_truth,并且ground_truth必须与对应图片的名称相同。同上一篇结果一样,我的输出为原始图片大小,但是不知道是不是因为图片不是正方形的,导致训练结果不好。编写一个脚本,重新规划数据集的路径规则,但是我稍微写复杂了一点,使用了类来操作。第二种(复杂):编写一个自己的Dataset,参考。第一种(简单):将数据集路径规则改成项目中。返回的必须是一个字典类型,可以阅读。本人此次使用的是第一种方法。运行时,参数部分直接输入。

xiaoxTai的博客 6479

java 原生类型(翻译自Java Tutorials)

原文出自http://www.cnblogs.com/ggjucheng/archive/2012/12/08/2809371.html 英文出自http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html java编程语言是静态类型,这表示变量使用前必须声明。这涉及阐明该变量的类型和名称,如你已经看到了: ...

weixin_34129145的博客 268

第零章 序 Chisel教程汇总

笔者目前西安交通大学微电子硕士在读,研究方向为数字集成电路设计与体系结构。鉴于目前RISC-V的火热程度,笔者参与了课题组的RISC-V项目,并了解、学习了颇受关注的新型硬件构建语言——Chisel,当前参与的神经网络加速器设计便是使用Chisel开发的。这是一门敏捷开发语言,据称要比传统的Verilog语言快很多。在亲身使用过后,笔者发现确实快了不少。这主要得益于Scala作为高级语言的简洁风格...

qq_34291505的博客 7万+

java原生类型_Java 基础 - 原生类型

更详细的说明,请参考:Java 原生类型与包装器类型深度剖析,https://blog.youkuaiyun.com/justloveyou_/article/details/52651211一. 原生类型与包装器类型1Byte = 8 Bits二. 自动转型与强制转型精度(低1-高5)1byte(1字节)Byteshort(2字节)Shortchar(2字节)Character2int(4字节)Integ...

weixin_26843605的博客 460

docker安装fastdfs+nginx配置

docker安装fastdfs+nginx,实现图片或文件的上传与下载

weixin_47325126的博客 2193

Vibe Draw:AI实现2D草图转3D建模,开源免费的设计师宝藏软件!

AI实现2D草图转3D建模,开源免费,设计师进来看一下?

Code_流苏:在代码中寻诗意,在实践中觅真知 2150

《Effective Java》解读第26条:请不要使用原生态类型

避免使用原生态类型(raw type),优先使用泛型(generic type)。

Cosmoshhhyyy的博客 463

图神经网络实战(21)——图神经网络的可解释性

在本节中,我们探讨了应用于图神经网络 (Graph Neural Networks, GNN) 的可解释性人工智能 (Explainable Artificial Intelligence, XAI) 技术。可解释性是许多领域的关键要素,可以帮助我们建立更好的模型。我们介绍了不同的局部解释技术,并重点讨论了 GNNExplainer (基于扰动的方法),在图分类任务中应用 GNNExplainer。

盼小辉丶的博客 9536

effective java 3 第26条 请不要使用原生态类型

定义: 声明中具有一个或多个类型参数的类或者接口,就是泛型。如List<E> 每一种泛型定义一组参数化类型,格式是C/I<实际类型参数>,如List<String> 是一个参数化类型 每一种泛型都定义一个原生态类型。如List<E>的原生态类型为List,其存在的主要作用是为了和之前的版本兼容 观点: 最好是在编译时期发现错误,所以泛型尽量不...

weixin_42192477的博客 258

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

学习记录:Effective Java -不要使用原生态类型,列表优于数组

消除非受检的警告可以提高代码的可读性和可维护性,同时避免潜在的运行时异常。在这个例子中,列表提供了更好的类型安全性和灵活性,因此通常情况下应该优先选择列表而不是数组。使用原生态类型可能导致编译时错误被延迟到运行时,因此应该尽量避免使用原生态类型。在Java中,列表(List)通常优于数组,因为列表具有更好的类型安全性和灵活性。通过使用泛型类型,编译器可以在编译时对类型进行检查,从而避免了潜在的运行时异常。避免使用原生态类型的方法是始终使用泛型类型。在Java中,原生态类型是指未经参数化的泛型类型,例如。

weixin_36639192的博客 713

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

一文读懂《Effective Java》第23条:不要在新代码中使用原生态类型

点击上方蓝字关注我们Java1.5 发行版增加了泛型(Generic)。泛型出现前,集合读取的每个对象都必须进行转换,如果不小心插入类型错误对的对象,运行时的转换处理会报错。泛型出现后,...

Diligence is the mother of success. 230

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Effective Java(第三版) 学习笔记 - 第五章 泛型 Rule26~Rule33

本文主要介绍泛型相关内容

weixin_44453385的博客 2207

Effective Java 第三版 中文翻译

effective-java-third-edition 介绍 Effective Java 第三版全文翻译,纯属个人业余翻译,不合理的地方,望指正,感激不尽! 目录 推荐序 前言 致谢 第一章 引言 第二章 创建和销毁对象   本章涉及创建和销毁对象,包括何时以及如何创建它们,何时以及如何避免创建它们,如何确保它们被及时销毁,以及如何管理在销毁之前必须进行的清理操作。 第1项:考虑静态工...

Coloured_Glaze的博客 3861

Effective Java 读书笔记

读书笔记

white_while的博客 1648

【读书笔记】Effective Java(泛型)

读书笔记-Effective Java(泛型)

我的博客 230

【Effective Java】第5章 读书笔记

第5章 泛型 26,请不要使用原生态类型 原生态类型:List,不带任何实际类型参数的的泛型名称 使用原生态类型是合法的,但是会失去泛型在安全性和描述性方面的所有方式。 List:不推荐,没有类型检查, List < Object >:可行,明确告诉编译器,可以接受任何类型 List < String >:可行,可以传给List,但是不能传给 List 泛型有子类型化规则。 无限制通配类型:Set <?>,不确定或者不关心实际类型参数 Set 与 Set<?&gt

qq_45050687的博客 230

Effective Java之泛型 总结

第23条 请不要在新代码中使用原生态类型 如果使用原生态类型 就失掉了泛型在安全性和表述性方面的所有优势 在类文字(class literal)中必须使用原生态类型 分类 术语 示例 参数化的类型 List&amp;lt;String&amp;gt; 实际类型参数 String 泛型 List&amp;lt;E&amp;gt; 形式类型参数 E 无限制通配符类型 List&a

勤劳的搬运工 285

Effective Java 高效编码阅读 笔记

函数参数可以传入类似listener的对象,目的是使用listener中的方法。如果使用匿名的参数,每一次调用会创建新的对象。可以将listener声明为成员变量,每次都复用同一个对象,并且可以使用静态域(static变量)。比如String类的域。

程序员小刘 1927

《Effective Java》泛型

[Java读书笔记] Effective Java(Third Edition) 第 5 章 泛型

朱嫑膘的博客 187

Effective java 总结4 - 泛型

声明中具有一个or多个类型参数的类或接口,就是泛型类或接口,统称泛型每个泛型都定义一个原生态类型(泛型名称,egList的原生态类型为List)泛型会自动进行编译期类型检查如果使用原生态类型,就会失去泛型在安全性和描述性方面的优势在不确定或者不在乎集合中类型时,使用无限制的通配符替代原生态类型Set必须在类文字中使用原生态类型List.class、String[].class,List.class则是非法的Set参数化类型List实际类型参数String。...

ym & blog 275

[Java读书笔记] Effective Java(Third Edition) 第 5 章 泛型

第 26 条:请不要使用原生态类型 声明中具有一个或多个类型参数的类或者接口,就是泛型(generic)。 例如List接口只有单个类型参数E, 表示列表的元素类型。这个接口全称List<E>,泛型类和接口统称为泛型(generic type)。 每一种泛型都定义一个原生态类型(raw type),即不带任何实际类型参数的泛型名称。 它的存在主要是为了兼容泛型之...

weixin_30702887的博客 181
上一篇: Java网络编程(三)Socket通信代码示例
下一篇: Effective Java笔记(27)消除非受检的警告
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值