当前位置: 首页 > news >正文

Effective Java学习笔记第27、28条原生态类型和非受检警告

目录

什么是泛型

泛型与编译器

不要轻易使用原生态类型

可以通过通配符类型来替代原生态类型

几个适合原生态类型的场景

消除非受检的警告

什么是非受检警告

如果无法消除警告


本书27-33条主要介绍泛型。首先介绍什么是泛型,它的应用场景是什么。然后重点介绍27和28条对于泛型编程的两点意见。一是不要轻易使用原生态类型,二是尽量消除非受检警告。

什么是泛型

顾名思义“泛型”指的是“多种类型”,声明中具有一种或多种类型参数的类或者接口成为泛型类或者泛型接口,这两者统称为泛型。常见的泛型接口包括List<T>和Set<T>。

泛型与编译器

首先要明确一个概念,泛型的受用范围仅限于编译阶段,编译完成后通过类型擦除进入执行的程序是不带有泛型信息的,下面举一个例子:

//定义一个简单的泛型
public class Myclass<T> {private T t;public void setT(T t) {this.t = t;}public T getT() {return t;}
}

通过javac将Myclass类编译后,由于类型擦除的原因当你将其反编译后相关的泛型信息会自动被替换成对应的实际类型,以下是通过javap反编译后的内容:

public class Myclass<T> {public Myclass();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic void setT(T);Code:0: aload_01: aload_12: putfield      #7                  // Field t:Ljava/lang/Object;5: returnpublic T getT();Code:0: aload_01: getfield      #7                  // Field t:Ljava/lang/Object;4: areturn
}

这里可以看到,泛型类中的两个泛型方法public void setT(T)和public T get()的输入和输出域原来的泛型T表示都被改换成了Ljava/lang/Object;(这里有的朋友会问为什么从还原的源码中仍然能够看到泛型,那是因为尽管类型擦除移除了泛型的具体类型信息,Java 编译器仍然会在字节码中添加一些额外的信息来支持运行时的类型安全性和泛型的正确使用。而反编译器会利用这些标注自动还原出原有的泛型信息)。

那么在编译阶段编译器会对于泛型做哪些操作来确保类型的安全性和正确性呢?

首先是类型检查一是类型的兼容性检查,编译器会检查泛型类型的使用是否符合类型兼容性规则。例如,确保泛型参数的使用与上下文中的类型相匹配。二是类型参数限制,如果泛型参数有类型界限(如 T extends Number),编译器会确保传递给泛型参数的实际类型符合这些界限。

其次是类型推断:编译器能够自动推断出泛型参数的具体类型,这使得开发者在某些情况下不必显式指定泛型参数的类型。比如对于泛型的初始化:

List<String> stringlist = new ArrayList<>();

这里ArrayList<>的初始化就不需要指定对应类型,编译器会自动推断。

类型擦除:编译器在编译阶段会去除泛型类型信息,将泛型类型转换为其对应的原始类型(例如,List<T> 变为 List)。

插入类型转换:为了确保类型安全,编译器会在适当的位置插入必要的类型转换语句。比如要在一个List<Object>中插入一个Integer,编译器就会讲Integer类型强制转换成Object。

了解了编译器对于泛型进行的各种操作,就可以很好的理解书中的27、28两条。

不要轻易使用原生态类型

原生态类型即隐去了泛型参数信息的类或者接口,比如List是List<String>的原生态类型。这里需要强调的是原生态类型是对应泛型的超类。

很好理解,使用原生态类型以后,类或者接口失去了泛型的属性,自然而然就会绕过编译器的各种类型检测功能,也就失去了泛型在安全性和描述性上所有的优势。也就是说如果程序错误插入了一个不同类型的元素,这个错误会一直保留到运行中当调用这个元素的方法出现问题的时候才会暴露问题;另一方面,编译器无法进行自动的类型转换,因此如果有相关的需求需要靠手动转换。

下面给一个使用了原生态类型的案例,这个原本计划只有Integer的List中混入了String类型,虽然编译器会给出Application.java uses unchecked or unsafe operations.的告警,但是并未显示任何实质性的警告,只有当程序运行中调用到“30”这个元素时才会真正显示错误;另一方面由于编译器未进行插入类型转换,因此对于每一个元素都要进行Integer的强制类型转换,否则系统默认会当作Object来处理。

public class Application {public static void main(String[] args) {List list = new ArrayList();list.add(21);list.add(23);list.add("30");for(Iterator i = list.iterator(); i.hasNext();){System.out.println((Integer)i.next()+1);}}
}
//编译警告
(base) chapter5$ javac Application.java
Note: Application.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.//运行异常
(base) chapter5$ java Application
22
24
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')

可以通过通配符类型来替代原生态类型

我们已经知道了原生态类型是有缺陷的,那当需要表示无限制类型的泛型时要如果实现呢?答案是无限制的通配符类型。

通配符类型是泛型功能的扩展,泛型将类和接口的应用类型设置为参数,而通配符就是允许把这个参数设置为一个集合。

public class WildcardDemo {public static void main(String[] args) {List<Integer> intList = new ArrayList<>();List<Number> numList = new ArrayList<>();// 使用上界通配符printNumbers(intList);printNumbers(numList);// 使用下界通配符addElement(intList, 100); // intList 是 Number 的子类型addElement(numList, 100); // numList 本身就是 Number 类型}public static void printNumbers(List<? extends Number> list) {for (Number n : list) {System.out.println(n);}}public static <T extends Comparable<T>> void addElement(List<? super T> list, T element) {list.add(element);}
}

这里printNumbers方法的输入参数是泛型接口List的上界通配符类型,代表该方法可以输入的泛型的类型参数可以是Number的子类。

这里要注意对于无限制通配符类型<?>,不能将任何元素(除null以外)放入,因为编译器无法确定相关元素是否符合类型要求(对于迭代器来说泛型的类型要求是一个未知类型)。

几个适合原生态类型的场景

在类文字中使用原生态类型:类文字是表示类的 Class 对象的一种特殊文字。List.class是合法的,而List<?>.class不合法。

通过instanceof操作符判断类型:用参数化类型使用instanceof操作符是非法的,但是无限制性通配符和原生态类型都是合法的。

消除非受检的警告

什么是非受检警告

首先警告是编译器在编译过程中发现的“异常”,编译过程中会有警告和错误两种“异常”,其中警告是对于运行过程中可能存在问题的提醒,但不影响编译的正常进行,而一旦存在错误将会提前终止编译。下面总结一下编译器的“异常”和程序运行过程中的“异常”。

非受检警告如果被无视就有可能在程序运行中出现各种异常或者错误情况,所以这也是为什么非受检异常无必要消除的原因(另外的弃用警告和未使用变量警告的影响会小一些,但最好还是也注意消除一下)。

如果无法消除警告

如果无法消除,同时证明引起警告的代码是类型安全的,才可以使用一个@SupressWarnings(“unchecked”)注解来禁止这条警告。

@SupressWarnings(“unchecked”)注解可以用在任何粒度的级别中,从单独的局部变量声明到整个类都可以,但是请尽量在尽可能小的范围内使用,否则可能会掩盖重要的警告(永远不要直接在类上使用)。

注意每次使用该注解时,都要添加一条注释,说明这么做为什么是安全的。

相关文章:

Effective Java学习笔记第27、28条原生态类型和非受检警告

目录 什么是泛型 泛型与编译器 不要轻易使用原生态类型 可以通过通配符类型来替代原生态类型 几个适合原生态类型的场景 消除非受检的警告 什么是非受检警告 如果无法消除警告 本书27-33条主要介绍泛型。首先介绍什么是泛型&#xff0c;它的应用场景是什么。然后重点介…...

javaEE和javaSE

引用自&#xff1a;https://developer.baidu.com/article/detail.html?id3312755 文章目录 前景描述javaSE简介使用场景 javaEE&#xff08;J2EE&#xff09;简介使用场景 结语 前景描述 javaEE和javaSE是java中比较常见的两个概念,但是又比较容易忘记&#xff0c;在此进行记…...

Leetcode 17.电话号码的字母组合

目录 题目 方法一 思路 代码 题目 17. 电话号码的字母组合 难度&#xff1a;中等 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对…...

位1的个数

编写一个函数&#xff0c;获取一个正整数的二进制形式并返回其二进制表达式中设置位的个数&#xff08;也被称为汉明重量&#xff09;。 示例 1&#xff1a; 输入&#xff1a;n 11 输出&#xff1a;3 解释&#xff1a;输入的二进制串 1011 中&#xff0c;共有 3 个设置位。示…...

RPA在政务服务中的挑战与解决方案

随着数字化时代的到来&#xff0c;数字政务的建设已成必然趋势&#xff0c;RPA作为数字化转型的重要工具之一&#xff0c;能够帮助政府单位快速实现业务流程的自动化和智能化&#xff0c;提高工作效率和质量&#xff0c;为建设数字政务提供强有力的支持&#xff0c;因此正被越来…...

RabbitMQ docker安装

后台配置文件 rabbitmq:image: rabbitmq:latestcontainer_name: rabbitmqports:- "5672:5672" # RabbitMQ server port- "15672:15672" # RabbitMQ management console portenvironment:RABBITMQ_DEFAULT_USER: adminRABBITMQ_DEFAULT_PASS: admin 若要打…...

关于vs调试的一些基本技巧方法,建议新手学习

文章目录 1.Debug 和 Release2.VS的调试快捷键3.对程序的监视和内存观察3.1监视3.2内存 4.编程常见错误归类4.1编译型错误4.2链接型错误4.3运行时错误 1.Debug 和 Release 在我们使用的编译器 vs 中&#xff0c;这个位置有两个选项&#xff0c;分别为Debug和Release&#xff0c…...

​MySQL——索引(二)创建索引(2)使用 CREATE INDEX 语句在已经存在的表上创建索引

若想在一个已经存在的表上创建索引&#xff0c;可以使用 CREATE INDEX 语句&#xff0c;CREATEINDEX语句创建索引的具体语法格式如下所示: CREATE [UNIQUEIFULLTEXTISPATIAL]INDEX 索引名 ON 表名(字段名[(长度)J[ASCIDESC]); 在上述语法格式中&#xff0c;UNIQUE、FULLTEXT 和…...

前端HTML总结

目录 前言 正文 head SEO body 网页的主要组成元素&#xff1a; body标签中常见的标签&#xff1a; 自闭合标签&#xff1a; 无语义标签&#xff1a; 特殊符号&#xff1a; 列表 子项&#xff1a; 样式修改&#xff1a; 定义列表&#xff1a; 语义化&#xff1…...

【动态规划】647. 回文子串

力扣链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 动规大法开始吟唱&#xff1a; dp[i][j]含义&#xff1a;从i到j的子串是否为回文子串 递推公式&#xff1a;当s[i] s[j]时 1. j-i<1时, dp[i][j]为true 2. 否则&#xff0c;若dp[i1][j-1]为true&#x…...

python-约瑟夫环(赛氪OJ)

[题目描述] n 个人&#xff08; 0,1,2,3,4...n−1 &#xff09;&#xff0c;围成一圈&#xff0c;从编号为 k 的人开始报数&#xff0c;报数报到 m 的人出队。 下次从出队的人之后开始重新报数&#xff0c;循环往复&#xff0c;当队伍中只剩最后一个人的时候&#xff0c;那个人…...

Less 教程:从入门到精通

Less 教程&#xff1a;从入门到精通 1. 引言 Less 是一种流行的动态样式表语言&#xff0c;它扩展了 CSS 的功能&#xff0c;使其更加强大和灵活。通过本教程&#xff0c;我们将深入探讨 Less 的基本概念、特性以及如何在项目中实际应用它。 2. Less 的基本概念 2.1 变量 …...

【VScode】如何在anaconda虚拟环境中打开vscode项目

文章目录 【必备知识】打开anaconda虚拟环境切换到项目工作目录激活anaconda虚拟路径让vscode从当前目录打开 【必备知识】 anaconda环境变量配置及配置python虚拟环境 https://blog.csdn.net/xzzteach/article/details/140621596 打开anaconda虚拟环境 切换到项目工作目录 …...

Flink任务提交流程和运行模式

任务提交流程 Flink 的提交流程随着部署模式、资源管理平台的不同&#xff0c;会有不同的变化。这里做进一步的抽象&#xff0c;形成一个大概高视角的任务执行流程图&#xff0c;如下&#xff1a; Flink按照集群和资源管理的划分运行模式有&#xff1a;Standalone、Flink On…...

【机器学习】 Sigmoid函数:机器学习中的关键激活函数

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Sigmoid函数&#xff1a;机器学习中的关键激活函数1. 引言2. Sigmoid函数定义3.…...

Vue+Element Plus后台管理主界面搭建实现

​ 续接Django REST Framework&#xff0c;使用Vite构建Vue3的前端项目 1. 后台管理系统主界面框架搭建 后台系统主界面搭建 新建后台管理文件目录 完成后台整体布局 // 1.主界面 index.vue<script setup lang"ts"></script><template><el-…...

JAVA—异常

认识异常&#xff0c;学会从报错信息中发现问题&#xff0c;解决问题。并学会构建自定义异常&#xff0c;提醒编程时注意 目录 1.认识异常 2.自定义异常 1.自定义运行时异常 2.自定义编译时异常 3.异常的处理 1.认识异常 异常就是代表程序出现的问题&#xff0c;用来查询B…...

常见八股面试题:Dubbo 和 Spring Cloud Gateway 有什么区别?

大家好&#xff0c;我是鸭鸭&#xff01; 此答案节选自鸭鸭最近弄的面试刷题神器面试鸭&#xff0c;更多大厂常问面试题&#xff0c;可以点击进行阅读哈&#xff01; 目前这个面试刷题神器刚出&#xff0c;有网页和小程序双端可以阅读&#xff01; 回归面试题&#xff01; …...

k8s分布式存储-ceph

文章目录 Cephdeploy-ceph部署1.系统环境初始化1.1 修改主机名&#xff0c;DNS解析1.2 时间同步1.3 配置apt基础源与ceph源1.4关闭selinux与防火墙1.5 **创建** ceph **集群部署用户** cephadmin1.6分发密钥 2. ceph部署2.1 **安装** ceph 部署工具2.2 **初始化** mon **节点**…...

Redis cluster集群部署

redis搭建集群模式、Cluster模式&#xff08;6节点&#xff0c;3主3从集群模式&#xff0c;添加删除节点&#xff09;_redis cluster节点带数据增减-CSDN博客...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...