【JavaSE】Java进阶知识一(泛型详解,包括泛型方法,协变,逆变,擦除机制)

目录

泛型

1. 什么是泛型

2.泛型方法

3.通配符上界(泛型的协变)

4.通配符下界(泛型的逆变)

5.泛型的编译(擦除机制)


泛型

        泛型:就是让一个类能适用于多个类型,就是在封装数据结构时能让封装的类型被各种类型使用所以引入了泛型的概念,虽然有了泛型,什么数据都可以放,但是更多情况下我们还是希望他只能持有一种数据类型。所以,泛型的主要目的:指定当前的容器,要持有什么类型的对象,让编译器去做检查。

1. 什么是泛型

语法格式如下:

泛型类<类型实参>变量名;//定义一个泛型类引用

new 泛型类<类型实参>(构造方法实参);//实例化一个泛型类对象

一般用<T>作为占位符 ,表示当前类是一个泛型类。Java中的泛型参数只能是引用类型,不能是基本类型,这与Java的泛型擦出机制有关。

 实例:

MyArray<Integer> list = new MyArray<Integer>();

*裸类型(Raw Type)      (这是一个泛型类但没有带着类型实参) 

MyArray list = new MyArray();

裸类型是为了兼容老版本的API保留机制,我们不要轻易使用。 

2.泛型方法

 泛型方法:定义一个泛型方法,我们需要在方法返回值前使用尖括号声明一个或多个泛型参数然在方法中就可以用到声明的泛型参数了,调用泛型方法时,我们不需要手动写出类型,编译器会根据你的调用,自动推导出具体类型。

静态泛型方法:泛型类有一个局限,静态方法和静态属性访问不了类上定义的泛型参数,静态泛型方法的定义和使用与普通泛型方法一致。

泛型类和泛型方法的使用场景:

当泛型参数需要在多个方法或成员属性间扭转,就使用泛型类,比如:集合。

当泛型参数只需要作用于某个方法,那就使用泛型方法。

3.通配符上界(泛型的协变)

泛型类型是具有不变性的,比如下面代码就是错误的:

Arraylist<Object> objectList;
ArrayList<String> stringList = new ArrayList<>();
objectList=stringList//这里会报错

objectList.add(new Shit());
String str = stringList.get(0);
//因为我们无法将一个object对象转化为string对象,所以在编译层面上面的赋值就会直接报错

 为了让泛型变得更灵活,Java引入了通配符:?,通过下面的代码来给大家介绍一下通配符的作用:

在不使用通配符时,因为泛型的不变性,下面这段代码会出现问题,就使代码非常不灵活。

public static double sum(List<Number> list){
   double result =0;
   for(Number number : list){
        result += number.doubleValue();
   }
    return result;
}

List<Double> doubleList = new ArrayList<>();
sum(doubleList)//这里会报错

我们可以使用通配符上界(?:extends T)来使代码更灵活

public static double sum(List<? extends Number> list){
   double result =0;
   for(Number number : list){
        result += number.doubleValue();
   }
    return result;
}

List<Double> doubleList = new ArrayList<>();
sum(doubleList)

这种写法也被叫做泛型的协变 。

4.通配符下界(泛型的逆变)

我们还可以使用通配符下界(?:super T)来使代码变得灵活,代码实例如下:

class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Plate<T> {
private T plate ;
public T getPlate() {
return plate;
}
public void setPlate(T plate) {
this.plate = plate;
}
}
public class TestDemo {
public static void main(String[] args) {
Plate<Fruit> plate1 = new Plate<>();
plate1.setPlate(new Fruit());
fun(plate1);
Plate<Food> plate2 = new Plate<>();
plate2.setPlate(new Food());
fun(plate2);
}
public static void fun(Plate<? super Fruit> temp){
// 此时可以修改!!添加的是Fruit 或者Fruit的子类
temp.setPlate(new Apple());//这个是Fruit的子类
temp.setPlate(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类
System.out.println(temp.getPlate());//只能直接输出
}
}

通配符的优缺点:

协变:放宽了对子类类型的泛型约束,但是缺点是不能对调用的参数进行写入数据只能进行读取数据。

逆变:放宽了对父类类型的泛型约束,但是缺点是不能对参数进行读取数据,只能写入数据。

5.泛型的编译(擦除机制)

擦除机制的实质就是,在编译阶段,Java的泛型类型可能是ArrayList<Integer>但是在java文件编译成字节码的过程中,泛型参数部分就被擦出了(泛型类,泛型方法的参数全部被替换成它的第一个上界或者顶级父类Object),在class文件中,无论参数是什么,JVM实际执行的代码类型其实是ArrayList<Object>类型,这也就引出了很多问题如下:

  1. 泛型参数只能是引用类型而不能是基本数据类型,因为基本数据类型无法被擦除成Object。
  2. 不能使用instanceof关键字进行泛型类型检测,因为在运行时所以的泛型类型都是裸类型。
  3. 泛型类型无法实例化类型参数T a=new T(),因为在运行时无法确定T的具体类型,也不知道T是否存在无参构造器。
  4. 无法实例化泛型数组T[] arry =new T[2];因为泛型最后都被擦除成Object数组,在使用时很容易发生类型转化异常,比如object转化不成string。

擦除机制是Java为了引入泛型这个语法而不得不做出的妥协之举,泛型语法是JDK5之后引入的,为了兼容老版本,不得不在编译阶段将泛型擦除成裸类型。但是在其他语言中,泛型的使用会非常自然且简单安全,在编写代码是我们要了解泛型擦除机制,否则可能会引发很多不必要的异常。

类型擦除是指在运行时对于JVM而言泛型参数被擦除掉了,并不代表泛型信息消失了,才class文件中泛型信息被以其他方式进行保存,我们依然可以在运行时通过反射的手段进行泛型类型检测。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

四、ensp配置ftp服务器实验

文章目录 实验内容实验拓扑操作步骤配置路由器为ftp server 实验内容 本实验模拟企业网络。PC-1为FTP 用户端设备&#xff0c;需要访问FTP Server&#xff0c;从服务器上下载或上传文件。出于安全角度考虑&#xff0c;为防止服务器被病毒文件感染&#xff0c;不允许用户端直接…

【什么是反射机制?为什么反射慢?】

✅ 什么是反射机制&#xff1f;为什么反射慢&#xff1f; ✅典型解析✅拓展知识仓✅反射常见的应用场景✅反射和Class的关系 ✅典型解析 反射机制指的是程序在运行时能够获取自身的信息。在iava中&#xff0c;只要给定类的名字&#xff0c;那么就可以通过反射机制来获得类的所有…

npm的常用使用技巧

npm是一个强大的工具&#xff0c;可以帮助你管理Node.js项目中的依赖项。以下是一些有用的npm使用技巧&#xff1a; 使用npm install命令&#xff1a;这个命令可以安装项目的依赖项。如果你想安装一个特定的版本&#xff0c;你可以使用npm install <package><version…

【FPGA】分享一些FPGA视频图像处理相关的书籍

在做FPGA工程师的这些年&#xff0c;买过好多书&#xff0c;也看过好多书&#xff0c;分享一下。 后续会慢慢的补充书评。 【FPGA】分享一些FPGA入门学习的书籍【FPGA】分享一些FPGA协同MATLAB开发的书籍 【FPGA】分享一些FPGA视频图像处理相关的书籍 【FPGA】分享一些FPGA高速…

基于Hexo+GitHub Pages 的个人博客搭建

基于HexoGitHub Pages 的个人博客搭建 步骤一&#xff1a;安装 Node.js 和 Git步骤二&#xff1a;创建Github Pages 仓库步骤二&#xff1a;安装 Hexo步骤三&#xff1a;创建 Hexo 项目步骤四&#xff1a;配置 Hexo步骤五&#xff1a;创建新文章步骤六&#xff1a;生成静态文件…

Appium Server 启动失败常见原因及解决办法

Error: listen EADDRINUSE: address already in use 0.0.0.0:4723 如下图&#xff1a; 错误原因&#xff1a;Appium 默认的4723端口被占用 解决办法&#xff1a; 出现该提示&#xff0c;有可能是 Appium Server 已启动&#xff0c;关闭已经启动的 Appium Server 即可。472…

推荐给前端开发的 5 款 Chrome 扩展

工欲善其事&#xff0c;必先利其器。Chrome 可能是前端开发中使用最多的浏览器。在日常开发中&#xff0c;下列几款 Chrome 扩展也许能让你的开发工作事半功倍 &#x1f680; Vue.js devtools ⚙️ vue 官方专为 vue 应用开发的调试工具。 通过使用它&#xff0c;你可以快速查看…

4.svn版本管理工具使用

1. 什么是SVN 版本控制 它可以记录每一次文件和目录的修改情况,这样就可以借此将数据恢复到以前的版本,并可以查看数据的更改细节! Subversion(简称SVN)是一个自由开源的版本控制系统。在Subversion管理下,文件和目录可以超越时空 SVN的优势 统一的版本号 Subversi…

prometheus二进制安装

1、在需要安装prometheus的目录下执行wget命令下载软件到本地&#xff0c;如我的路径是/opt/module/prometheus wget https://github.com/prometheus/prometheus/releases/download/v2.34.0/prometheus-2.34.0.linux-amd64.tar.gz正在解析主机 objects.githubusercontent.com …

Fireblock:为Dapp实现可编程隐私

1. 引言 Fireblock network为Cosmos生态应用链。并于2023年10月宣布完成pre-seed轮250万美金融资。 其定位为实现&#xff1a; 有条件解密可编程隐私 Fireblock使用的密码学方案有&#xff1a; distributed key generation&#xff08;DKG&#xff09;Identity-based encry…

华为云Stack 8.X 流量模型分析(二)

二、流量模型分析相关知识 1.vNIC ​ 虚拟网络接口卡(vNIC)是基于主机物理 NIC 的虚拟网络接口。每个主机可以有多个 NIC&#xff0c;每个 NIC 可以是多个 vNIC 的基础。 ​ 将 vNIC 附加到虚拟机时&#xff0c;Red Hat Virtualization Manager 会在虚拟机之间创建多个关联的…

MySQL创建member表失败

最近在做一个项目&#xff0c;在台式机上可以跑通&#xff0c;也测试了各个已完成的接口&#xff0c;提交到了GitHub后想着用宿舍的电脑跑一下&#xff0c;在测试member表相关接口时就出错了。报了SQL语法错误&#xff0c;但SQL语句很简单&#xff0c;就根据手机号查询不至于出…

Redux与React环境准备、实现counter(及传参)、异步获取数据

环境说明&#xff1a; 一&#xff1a;说明 在React中使用redux&#xff0c;官方要求安装两个其他插件&#xff1a;Redux Toolkit和react-redux 1. Redux ToolKit(RTK) - 官方推荐编写Redux逻辑的方式&#xff0c;是一套工具的集合集&#xff0c;简化书写方式 &#xff08;简化…

【ps】新手 学 PS一本通

第一章 添加图像边框 1. 导入一张图片 2.选择 图像-画布大小 例&#xff1a;原图&#xff1a;720x820 填写画布大小&#xff1a;820x920 可以增加一个100x100的边框。 画布扩展颜色是扩展的颜色。 标尺工具 视图>标尺 或者使用 CTRL R 网格工具 视图-显示-网格 …

JavaOOP篇----第十五篇

系列文章目录 文章目录 系列文章目录前言一、有没有可能两个不相等的对象有相同的hashcode二、拷贝和浅拷贝的区别是什么?三、static都有哪些用法?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通…

【BBuf的CUDA笔记】十,Linear Attention的cuda kernel实现解析

欢迎来 https://github.com/BBuf/how-to-optim-algorithm-in-cuda 踩一踩。 0x0. 问题引入 Linear Attention的论文如下&#xff1a; Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention&#xff1a;https://arxiv.org/pdf/2006.16236.pdf 。官方…

【docker】安装mysql

查看可用的 mysql版本 docker search mysql拉取 MySQL最新镜像 docker pull mysql:latest 查看镜像 docker images 运行容器 docker run -it -d --name mysql-demo -m 500m -p 3309:3306 -v /test1/mysql/data:/var/lib/mysql -v /test1/mysql/config:/etc/mysql/conf.d -…

python实现元旦多种炫酷高级倒计时_附源码【第19篇—python过元旦】

文章目录 &#x1f30d;python实现元旦倒计时 — 初级(控制台)⛅实现效果&#x1f30b;实现源码&#x1f31c;源码讲解 &#x1f30d;python实现元旦倒计时 — 中级(精美动态图)⛅实现效果&#x1f30b;实现源码&#x1f31c;源码讲解 &#x1f30d;python实现元旦倒计时 — 高…

32. 深度学习进阶 - Transfer Learning

Hi&#xff0c;你好。我是茶桁。 之前的课程中&#xff0c;咱们学习了CNN的原理&#xff0c;学习了pooling, fully connected是做什么的。还了解了理论上简单的模型也是可以做事情的&#xff0c;只不过在特定的一些情况下要解决问题的时候简单方法效果不太好&#xff0c;所以用…

STM32位带

GPIO_SetBits(GPIOF,GPIO_Pin_9);修改为PFout(9)1; GPIO_ResetBits(GPIOF,GPIO_Pin_9);修改为PFout(9)0; 位带的定义&#xff1a; 支持了位带操作后&#xff0c;可以使用普通的加载/存储指令来对单一的比特进行读写。在CM3 中&#xff0c;有两个区中实现了位带。其中一个是S…