Java从入门到放弃 之 泛型

Java从入门到放弃 - 泛型

  • 引入泛型的背景
    • 怎么解决这个问题?
      • 解决方式一
      • 解决方式二
    • 使用泛型
      • 代码案例一
      • 代码案例二
    • 通配符的使用
      • extends 通配符
      • super 通配符
      • 对比extends和super通配符
    • 泛型的原理

引入泛型的背景

在Java中当我们使用容器存储元素的时候(建议先了解一部分Java容器知识),实际上使用的是Object存储的元素,比如下面ArrayList部分源码

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {private static final long serialVersionUID = 8683452581122892189L;private static final int DEFAULT_CAPACITY = 10;private static final Object[] EMPTY_ELEMENTDATA = new Object[0];private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];transient Object[] elementData;private int size;
}

从这部分源码我们可以看出ArrayList使用的是Object[] 数组存储的元素,但是这里就引出了一个问题。我们使用Object存储,是不是就把原始类型就给丢弃了。导致我们在从容器中拿出元素之后,想要进一步使用元素还需要向下转型,这让代码变得麻烦、臃肿、不安全,并且容易发生类型转换异常。

怎么解决这个问题?

因为存储的时候是通用的容器,用Object没有问题,但是使用的时候我们还是需要知道原始类型。

解决方式一

使用特定容器不用通用容器不就好了, 比如存储String我们就写一个存储String数组的ArrayList,存储Integer 就写一个存储Integer数组的ArrayList

public class StringArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {private static final long serialVersionUID = 8683452581122892189L;private static final int DEFAULT_CAPACITY = 10;private static final String[] EMPTY_ELEMENTDATA = new Object[0];private static final String[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];transient String[] elementData;private int size;
}
public class IntegerArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {private static final long serialVersionUID = 8683452581122892189L;private static final int DEFAULT_CAPACITY = 10;private static final Integer[] EMPTY_ELEMENTDATA = new Object[0];private static final Integer[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];transient Integer[] elementData;private int size;
}

这样可不可以呢? 当然是可以的,但是有没有问题? 聪明的你,看上面的代码一下子就看出来了,这样写,会产生一大堆重复代码,而且还要写一大堆类。 这肯定不是一种很好的实现方式。

所以有没有更好的实现方式呢?

解决方式二

Java是强类型编程语言,强类型的意思就是在编写代码的时候必须要明确变量的类型,不然的话编译不通过。但是有时候我们不知道某些变量的具体类型是什么,只能用公共父类Object类型,这就是容器或者上面代码ArrayList里面为啥使用Object[]数组存储元素,因为我们不知道具体要存储的数据类型。其实观察上面两段代码,我们其实发现大部分都是相同的,只有标记用什么类型存储数组的地方是不一样的。解决这个问题的方式就是我们在程序的某个地方标识完全未知的类型,使程序顺利通过编译,等到使用的时候确定具体类型。 泛型(Generics)就是泛化的类型,即使用 表示一个暂时没有确定的类型。

使用泛型

接下我们就通过实际代码了解如何使用泛型

代码案例一

        public class Pair<T> {T first;T second;public Pair(T first, T second){this.first = first;this.second = second;}public T getFirst() {return first;}public T getSecond() {return second;}}
        Pair<Integer> minmax = new Pair<Integer>(1,1);Integer min = minmax.getFirst();Integer max = minmax.getSecond();

通过使用泛型,我们达到了使得Pair类的代码和它处理的数据类型不是绑定的,具体类型可以变化。也就是说Pair类即可以处理Integer也可以处理String等等

代码案例二

类型参数还可以是多个

        public class Pair<K, V> {K first;V second;public Pair(K first, V second){this.first = first;this.second = second;}public K getFirst() {return first;}public V getSecond() {return second;}}
        Pair<Integer, String> test = new Pair<Integer, String>(1,"test");Integer num = test.getFirst();String test = mintest.getSecond();

Pair<Integer, String> test = new Pair<>(1,“test”) 这样写,因为编译器可以自动推断泛型类型

通配符的使用

ArrayList 不是 ArrayList的父类, 你可以这样理解。 动物类是人类的父类, 装动物类的汽车类不是装人类的汽车类的父类。

extends 通配符

        public void addAll(ArrayList<Number> array) {}

对于这个把所有Number的list放到一个其他容器的方法,Integer是number的子类,Number能放的 Integer也可以放进去。但是上面我们说的继承关系可以知道。ArrayList 跟ArrayList 没有父子关系,直接把ArrayList 传入这个方法,会报错的。
怎么办? 这时候可以使用extends关键字

        public void addAll(ArrayList< ? extends Number> array) {}

这样使得方法接收所有泛型类型为Number或Number子类的ArrayList类型了,这样就解决了我们的问题。

super 通配符

ArrayList<? super Integer>表示,方法参数接受所有泛型类型为Integer或Integer父类的ArrayList类型。

对比extends和super通配符

  • <? extends T>允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
  • <? super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。

一个是允许读不允许写,另一个是允许写不允许读。

泛型的原理

这个是我们写的泛型类,编译器看到的就是我们写的源代码,就是下面这个案例写的这样,

        public class Pair<T> {T first;T second;public Pair(T first, T second){this.first = first;this.second = second;}public T getFirst() {return first;}public T getSecond() {return second;}}

实际上JVM执行的时候看到的代码是,

        public class Pair<T> {Object first;Object second;public Pair(Object  first, Object  second){this.first = first;this.second = second;}public Object  getFirst() {return first;}public Object  getSecond() {return second;}}

使用泛型的地方,编译器看到的源码,

        Pair<Integer> minmax = new Pair<Integer>(1,1);Integer min = minmax.getFirst();Integer max = minmax.getSecond();

这段代码实际上JVM看到的是

        Pair minmax = new Pair(1,1);Integer min = (Integer)minmax.getFirst();Integer max = (Integer)minmax.getSecond();

所以Java是通过擦除法实现泛型

  • 编译器会把 类似泛型地方的写法都会写成Object
  • 编译器根据 实现安全的类型转换

所以实际上Java的泛型是由编译器在编译时实行的,编译器泛型视为Object处理,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。

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

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

相关文章

Flutter-Web首次加载时添加动画

前言 现在web上线后首次加载会很慢&#xff0c;要5秒以上&#xff0c;并且在加载的过程中界面是白屏。因此想在白屏的时候放一个加载动画 实现步骤 1.找到web/index.html文件 2.添加以下<style>标签内容到<head>标签中 <style>.loading {display: flex;…

动态规划子数组系列一>最长湍流子数组

1.题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public int maxTurbulenceSize(int[] arr) {int n arr.length;int[] f new int[n];int[] g new int[n];for(int i 0; i < n; i)f[i] g[i] 1;int ret 1;for(int i 1; i < n-1; i,m. l.kmddsfsdafsd){int…

win10 禁止更新

一、winR 输入 regedit 二、输入注册列表路径&#xff1a; &#xff08;1&#xff09;计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings &#xff08;2&#xff09;按照格式&#xff0c;创建文件命名: FlightSettingsMaxPauseDays &#xff08;3&…

传奇996_36——背包图标,物品位置问题

绑定位置不对位 CTRLF9背包物品文件&#xff0c;也就是bag_item文件夹的bag_item.lua文件&#xff0c;这个小框和大框的相对位置会影响那个绑定图标,就是背包物品组合的标签和下面子标签的相对位置 背包物品偏移到看不见 原因&#xff1a;CTRLF9背包物品文件&#xff0c;也就…

springboot3如何集成knife4j 4.x版本及如何进行API注解

1. 什么是Knife4j knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案, 取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍!knife4j的前身是swagger-bootstrap-ui,swagger-bootstrap-ui自1.9.6版本后,正式更名为knife4j为了契合微服务的架构发展,由于原来…

机械设计学习资料

免费送大家学习资源&#xff0c;已整理好&#xff0c;仅供学习 下载网址&#xff1a; https://www.zzhlszk.com/?qZ02-%E6%9C%BA%E6%A2%B0%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83SOP.zip

【大数据学习 | Spark-Core】RDD的概念与Spark任务的执行流程

1. RDD的设计背景 在实际应用中&#xff0c;存在许多迭代式计算&#xff0c;这些应用场景的共同之处是&#xff0c;不同计算阶段之间会重用中间结果&#xff0c;即一个阶段的输出结果会作为下一个阶段的输入。但是&#xff0c;目前的MapReduce框架都是把中间结果写入到HDFS中&…

Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复

目录 安装包 flume的部署 负载均衡测试 故障恢复 安装包 在这里给大家准备好了flume的安装包 通过网盘分享的文件&#xff1a;apache-flume-1.9.0-bin.tar.gz 链接: https://pan.baidu.com/s/1DXMA4PxdDtUQeMB4J62xoQ 提取码: euz7 --来自百度网盘超级会员v4的分享 ----…

B站直播模块解读——MVVM类似物

Model层: 数据类及其Converter Service接口lmpl实现类 (1)Scoket广播接受服务端下发数据 或在repository类中还是利用Socket广播 (2)业务接口接收服务端下发数据 将所有Service实现类注入LiveAppServiceManager统一管理 ViewModel层&#xff1a; ViewModel从LiveAppService…

Hive基础面试-如何理解复用率的

1. 模型的复用率你们是怎么做的&#xff1f; 简单直白的说就是你的模型复用率如何&#xff0c;在业务方是否认可该模型&#xff0c;也是衡量模型建设的一个标准&#xff0c;复用率数&#xff1a;数仓模型涉及的核心是追求模型的复用和共享&#xff0c;引用系数越高&#xff0c;…

eduSRC挖洞思路

声明 学习视频来自 B 站UP主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 ✍&#x1f3fb;作者简介&#xff1a;致…

Banana Pi BPI-CanMV-K230D-Zero 采用嘉楠科技 K230D RISC-V芯片设计

概述 Banana Pi BPI-CanMV-K230D-Zero 采用嘉楠科技 K230D RISC-V芯片设计,探索 RISC-V Vector1.0 的前沿技术&#xff0c;选择嘉楠科技的 Canmv K230D Zero 开发板。这款创新的开发板是由嘉楠科技与香蕉派开源社区联合设计研发&#xff0c;搭载了先进的勘智 K230D 芯片。 K230…

昆山网站建设在移动互联网时代的作用

在当今的移动互联网时代&#xff0c;昆山网站建设的重要性愈加凸显。随着智能手机和移动设备的普及&#xff0c;用户获取信息和进行消费的方式发生了根本性的变革。企业在此背景下&#xff0c;必须重新审视自己的在线形象和运营策略&#xff0c;以适应这一变化带来的挑战和机遇…

接上一主题,C++14中如何设计类似于std::any,使集合在C++中与Python一样支持任意数据?

这篇文章的重点是C多态的应用&#xff0c;但是如果你是C新手&#xff0c; 你需要了解以下C知识&#xff1a; 类 构造函数 拷贝构造函数 虚拟函数 纯虚拟函数 析构函数 类的继承 运算符重写 模板类 模板参数 数组 数组的传递 指针与动态内存分配 Python&#xff1a; s …

SpringBoot3与JUnit5集成测试

你可以在 Spring Boot 3 中轻松设置和运行 JUnit 集成测试。合理使用 Spring 提供的注解和工具&#xff0c;可以确保测试的高效性和可靠性。以下是集成测试的步骤和示例&#xff1a; 1. 添加依赖 在 pom.xml 中添加 Spring Boot Starter Test 依赖&#xff0c;它包含 JUnit 5 …

5.STM32之通信接口《精讲》之USART通信---实验串口接收程序

根据上节&#xff0c;我们一已经完成了串口发送程序的代码&#xff0c;并且深入的解析探索了串口的原理&#xff0c;接下来&#xff0c;Whappy小编将带领大家进入串口接收程序的探索与实验&#xff0c;并将结合上一节串口发送一起来完成串口的发送和接收实验。 上来两张图 上图…

微服务即时通讯系统的实现(服务端)----(1)

目录 1. 项目介绍和服务器功能设计2. 基础工具安装3. gflags的安装与使用3.1 gflags的介绍3.2 gflags的安装3.3 gflags的认识3.4 gflags的使用 4. gtest的安装与使用4.1 gtest的介绍4.2 gtest的安装4.3 gtest的使用 5 Spdlog日志组件的安装与使用5.1 Spdlog的介绍5.2 Spdlog的安…

数字IC后端实现时钟树综合系列教程 | Clock Tree,Clock Skew Group之间的区别和联系

Q: Clock&#xff0c;Clock Tree和Skew Group有何区别&#xff1f;Innovus CCOPT引擎是如何使用这些的&#xff1f; Clock是时序约束SDC中的时钟定义点。 create_clock -name clk_osc -period $period_24m [get_ports xin_osc0_func] 时钟树综合(Clock Tree Synthesis)之前应…

tcpdump抓包 wireShark

TCPdump抓包工具介绍 TCPdump&#xff0c;全称dump the traffic on anetwork&#xff0c;是一个运行在linux平台可以根据使用者需求对网络上传输的数据包进行捕获的抓包工具。 tcpdump可以支持的功能: 1、在Linux平台将网络中传输的数据包全部捕获过来进行分析 2、支持网络层…

青少年编程等级考试C++一级,硬币反转问题

代码 #include<iostream>using namespace std;bool a[300];int main(){ int n,m; cin >> n >> m; for(int i 1;i < m;i) { for (int j 1;j < n;j) { if( j % i 0) { a[j] !a[j];…