【Java数据结构】泛型详解+图文,通配符上界、下界

2023-11-16

0. 泛型的本质

泛型的本质:泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数
化。
总的来说,泛型就是把类型参数化了,即给类型一个指定的参数,在使用时指定参数具体的值,这个类型就可以在使用的时候决定了,这样就可以编写应用于多种类型的代码。

泛型代码注释List<>给String类型,代表List这个集合操作的是String类型的数据,且,=后面的ArrayList<>,这里的<>里的值可以省略。

在这里插入图片描述

0. 泛型的目的

泛型的目的:是指定当前容器,要持有什么类型的对象,让编译器去做检查,检查类型的安全性,并且所有的强制转换都是隐式转换的。此时,就需要把类型,作为参数传递,提高了代码的复用性。

作用
1.安全性 2.消除转换 3. 提高性能 4. 复用性

1. 泛型的语法

class 泛型类的名称<类型形参列表>{
	//代码块内使用类型参数
	}
class Student<T1,T2....Tn>{

}
  • 泛型的继承
class 泛型类型名称<类型形参列表> extends 继承类<类型形参列表>

//示例
class Mike<Integer> extends Student<Integer>{
	
}
//可以只是用部分类型参数
class Rose<T1,T2..Tn> extends Student<T1>{
	
}

1.1 泛型的使用

泛型类 public class Student <T1,T2...>{...;}
泛型方法 public T func(T[] arr,T root){....;}
泛型接口 public calss Student<T1> implements Comparable<T1>{...;}
  • 泛型类代码实例:
    在这里插入图片描述
T代表占位符,表示这个类或者是变量、方法是一个泛型,需要传类型参数,就好比一个函数有一个参数,你调用它需要给它传参,否则就会报错,这个T其实就是形参名,可以改成E、K...V、T都可以

了解:【命名规范】类型形参一般使用一个大写字母表示,常用的名称有:

E 表示Element(元素)
K 表示Key(钥匙、键)
V 表示Value(值)
N 表示Number(数)
T 表示Type(类型)
S,U,V等 第二,第三,第四个类型
  1. 注释1处:不能直接new泛型的数组
    我是先new一个Object数组,然后强制转换为T类型
T[] arr = new T[10]//error
  1. 注释2处:类型(Student)后加入< Integer >是指定当前类型,new Student<>,这里的<>内可加也可不加Integer
  2. 注释3处:不需要强制类型转换,Integer是包装类,可以自动拆箱和装箱。这也就提高了效率。
  3. 注释4处:代码编译报错,因为在注释2处指定类的当前类型是Integer,此时在注释4处,存放元素时,编译器就会帮助我们进行类型检查。

2. 包装类

在JAVA中,由于基本类型不是继承于Object,为了在泛型代码中可以支持基本类型,JAVA给每个基本类型都对应了一个包装类。

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
  • 需要特别注意的是:int和char的包装类是Integer和Character,其他的基本类型的包装类都是基本类型首字母大写。

2.1 装箱和拆箱

int a = 10;

/*	
		装箱操作
*/
Integer b = Integer.value(a); //调用value方法
Integer c new Integer(a);//调用构造方法

/*
		拆箱操作
*/
int d = c.intValue();

在下面几行行代码,编译器自动调用了Integer.valueof(),属于自动装包和拆包。

在这里插入图片描述

2.2.1练习题

  • 【挖坑:】下列代码输出什么,为什么?
public static void main(String[] args) { 
    Integer a = 100; 
    Integer b = 100; 
    Integer c = 200; 
    Integer d = 200; 
    System.out.println(a == b); 
    System.out.println(c == d); 
}

3 .泛型如何编译

  • 擦除机制

在编译的过程中,将所有的T都替换为Object的这种机制,称为:擦除机制。JAVA的泛型机制是在编译时实现的,编译器生成的字节码在运行期间并不包含泛型的类型信息(运行时没有泛型这么一说了)。

泛型擦除机制的文章介绍:
链接

在这里插入图片描述

  • 思考:
  1. Why? T[] T = new T[10];是不对的。编译的时候替换为Object,不是相当于:Object[] T = new Object[10];
  2. 类型擦除:一定把T擦除给Object吗?
  • 解惑:
解答 1.
泛型数组不能实例化

T[]擦除成了Object,Object是不能用其他类型接收的,因为Object可以存各种类型,把Object里的元素取出来转换成某个具体元素,是不安全的,JAVA不支持这么做。
数组和泛型之间有一个重要的区别就是:它们是如何强制执行类型检查。 数组在运行时存储和检查类型信息,而泛型是在编译时检查类型错误。
所以:泛型是不能实例化泛型类型数组。前面我写的T[] t = (T[]) new Obeject[10];其实也是错的。
在这里插入图片描述

解答2.
一定是都擦除成了Object,前面我已经说过了。

4.泛型的上界

class 泛型类名称 <类型形参> extends<类型形参> {
    //....
}
  • 示例:

     class Myarray{
        //..
    }
     class Int extends Myarray{
        //..
    }
     class Long extends Int{
        //...
    }
    
    public class Maths<E extends Myarray>{
        //...
       
    }
    

    解释:Maths<E extends Myarray>E只能是Myarray的子类,也就是说**E接受的类型实参只能是Myarray的子类**。

  • 再看一个例子:

     class Student<E extends Number>{
         E[] arr;
        E Name;
    }
    public class Test {
        public static void main(String[] args) {
        	Student<Integer> in;//1
            Student<String>  str;//2
    	}
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6YVc6XOJ-1665319877790)(C:\Users\COFFEEWEN\AppData\Roaming\Typora\typora-user-images\image-20221009194623454.png)]

    **解释:**注释1处,编译正确,因为IntegerNumber的子类型,注释2处,编译错误,因为String不是Number的子类型。

    • 了解:Number是 java.lang包下的一个抽象类,提供了将包装类型拆箱成基本类型的方法,所有基本类型的包装类型都继承了该抽象类,并且是final声明不可继承改变;

    • 了解:没有指定类型边界 E,可以视为 E extends Object

5. 通配符

通配符:
?用于在泛型的使用,即为通配符
<?>无边界通配符
<? extends E>上界的通配符,E为上界
<? super E>下界的通配符,E为下界

通配符是用于解决泛型之间引用传递问题的特殊语法,更为灵活,多用于扩大参数的范围。

  • 示例:
class Fruits<T>{
    private T fruits;
    
    public T getFruits(){
        return this.fruits;
    }
    
    public void setFruits(T fruits){
        this.fruits = fruits;
    }
}

public class Demo {
    public static void main(String[] args) {
        Fruits<String> fruits1 = new Fruits<>();
        fruits1.setFruits("苹果");
        put(fruits1);//输出
   
        Fruits<Integer> fruits2 = new Fruits<>();
        fruits2.setFruits(100);
        put(fruits2);
    }

    public static void put(Fruits<?> tmp){//1.
        System.out.println("这个水果是:"+tmp.getFruits());
        tmp.setFruits("banana");//2.无法修改
        tmp.setFruits(66);//3.无法修改       
        Fruits ret = tmp.getName();//4.无法接收

    }
}

在这里插入图片描述

解释:

  1. 注释1:处 put(Fruits<?> tmp)使用无边界通配符,说明它是可以接受任意类型,所以当不同类型的fruits1和fruits2调用put进行传参时,并不会报错。

  2. 注释2处、3处:由于是可以接受任意类型的,tmp是不确定类型的,不允许进行修改操作。比如,你此时传入了一个String类型的(传参时不会报错),可是你代码里写的是tmp.setFruits(66)设置的是int型,这样是很不安全的,所以编译器直接不允许你这么写。

  3. 注释4处:传入的参数类型是不确定的,不能用Fruits来接收。

5.1通配符上界

class Apple extends Fruits{
    
}
class Banana extends Fruits{
    
}
class Fruits extends Meat{
    
}
class Meat{
    
}
class  Food<T>{
    private T name;
    public T getName(){
        return this.name;
    }
    public void setName(T name){
        this.name = name;
    }
}

public class Demo {

    public static void main(String[] args) {
        Food<Apple> appleFood = new Food<>();
        //因为Food<T> 给的类型是Apple,所以你只能给相同类型的数据
        appleFood.setName(new Apple());
        fun(appleFood);
        
        Food<Banana> bananaFood = new Food<>();
        bananaFood.setName(new Banana());
        fun(bananaFood);
    }
    public static void fun(Food<? extends Fruits> tmp){//1.
        System.out.println(tmp.getName());
        tmp.setName(new Banana());//2.无法修改
        tmp.setName(new Apple());//3.无法修改
        Fruits ret = tmp.getName();//读取数据

    }

解释:

  1. 注释1处:使用了上界通配符,表示可以传入的实参类型是Fruits和Fruits的子类型
  2. 注释2、3处:由于是不确定类型的,所以无法修改,因为tmp接收的是Fruits和它的子类,此时存储的元素类型应该是哪个子类,是无法确定的,所以设置会报错!但是可以获取元素,因为ret的类型的Fruits,是所有能传入参数的父类。
  3. 通配符上界:可以读取数据,不能写入数据

5.2通配符下界

public static void main(String[] args) {
        Food<Fruits> f1 = new Food<>();
        f1.setName(new Fruits());
        fun(f1);
        
        Food<Meat> f2 = new Food<>();
        f2.setName(new Meat());
        fun(f2);
    }
public static void fun(Food<? super Fruits> tmp){//1.
        System.out.println(tmp.getName());//只能直接输出
        tmp.setName(new Banana());//2.可以修改
        tmp.setName(new Fruits());//3.可以修改
        Fruits ret = tmp.getName();//4.不能读取数据

    }

解释:

  1. 注释1处:使用的是通配符下界,表示接受的参数只能是Fruits或者是Fruits的父类。
  2. 注释2、3处:可以修改,因为tmp的类型是Fruits或者它的父类,只能设置Fruits的子类或者它本身。
  3. 注释4处:如果此时传入的参数是Fruits的父类,用Fruits类型来接收是不安全的,换句话说,传入的参数是不确定是哪个父类,不能接收。
  4. 通配符下界:不能读取数据,可以写入数据。
总结
<?>无边界通配符 可以传入任意实参类型,不能写入,不能读取,只能输出。
<? extends 上界> 可以传入实参类型是上界或者上界的子类,不能写入,可以读取。
<? super 下界> 可以传入实参类型是下界或者下界的父类,不能读取,可以写入数据。

配符下界:不能读取数据,可以写入数据。

有坑填坑

  • 填坑:
public static void main(String[] args) { 
    Integer a = 100; 
    Integer b = 100; 
    Integer c = 200; 
    Integer d = 200; 
    System.out.println(a == b); //1.
    System.out.println(c == d); //2.
}

解释:

  • 注释1处:输出true;
  • 注释2处:输出false;
  • Why?
    Integer源码有一个cache数组事先存储了【-128,127】之间的数据,总共256个数据,这些数据被static final修饰,是不能改变的。当赋的值是在这个区间里,会直接在这个数组里读取,也就是变量a和b,指向了同一个对象,所以输出true。当超过了这个区间,每次都会重新new一个对象,所以输出false
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Java数据结构】泛型详解+图文,通配符上界、下界 的相关文章

  • Java 中的 XPath 节点集

    我在 eclipse 中有这段代码 NodeSet nodes NodeSet xPath evaluate expression inputSource XPathConstants NODESET 它给我 NodeSet 上的编译时错误
  • 如何在 JFace 的 TableViewer 中创建复选框?

    我创建了一个包含两列的 tableViewer 我想将其中一列设为复选框 为此 我创建了一个 CheckBoxCellEditor 但我不知道为什么它不起作用 名为 tableName 的列显示其值正常 色谱柱规格如下 String COL
  • ElasticBeanstalk Java,Spring 活动配置文件

    我正在尝试通过 AWS ElasticBeanstalk 启动 spring boot jar 一切正常 配置文件为 默认 有谁知道如何为 java ElasticBeanstalk 应用程序 不是 tomcat 设置活动配置文件 spri
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • manifest.mf 文件的附加内容的约定?

    Java JAR 中的 MANIFEST MF 文件是否有任何超出 MANIFEST MF 约定的约定 JAR规范 http download oracle com javase 1 4 2 docs guide jar jar html
  • JNI 不满意链接错误

    我想创建一个简单的 JNI 层 我使用Visual studio 2008创建了一个dll Win 32控制台应用程序项目类型 带有DLL作为选项 当我调用本机方法时 出现此异常 Exception occurred during even
  • IntelliJ IDEA 创建的 JAR 文件无法运行

    我在 IntelliJ 中编写了一个跨越几个类的程序 当我在 IDE 中测试它时它运行良好 但是 每当我按照教程将项目制作成 jar 可执行文件时 它就不会运行 双击 out 文件夹中的文件时 该文件不会运行 并显示 无法启动 Java J
  • 在浏览器中点击应用程序时播放框架挂起

    我正在 Play 中运行一个应用程序activator run 也许 5 次中有 3 次 它会挂起 当我去http localhost 9000 它就永远坐在那里旋转 我看到很多promise timed out错误也 我应该去哪里寻找这个
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • HDFS:使用 Java / Scala API 移动多个文件

    我需要使用 Java Scala 程序移动 HDFS 中对应于给定正则表达式的多个文件 例如 我必须移动所有名称为 xml从文件夹a到文件夹b 使用 shell 命令我可以使用以下命令 bin hdfs dfs mv a xml b 我可以
  • jdbc4.MySQLSyntaxErrorException:数据库中不存在表

    我正在使用 SpringBoot 开发一个网络应用程序 这是我的application properties文件来指定访问数据库的凭据 spring datasource driverClassName com mysql jdbc Dri
  • Microsoft Graph 身份验证 - 委派权限

    我可以使用 Microsoft Graph 访问资源无需用户即可访问 https developer microsoft com en us graph docs concepts auth v2 service 但是 此方法不允许我访问需
  • 迁移到 java 17 后有关“每个进程的内存映射”和 JVM 崩溃的 GC 警告

    我们正在将 java 8 应用程序迁移到 java 17 并将 GC 从G1GC to ZGC 我们的应用程序作为容器运行 这两个基础映像之间的唯一区别是 java 的版本 例如对于 java 17 版本 FROM ubuntu 20 04
  • 检查 Android 手机上的方向

    如何查看Android手机是横屏还是竖屏 当前配置用于确定要检索的资源 可从资源中获取Configuration object getResources getConfiguration orientation 您可以通过查看其值来检查方向
  • Java直接内存:在自定义类中使用sun.misc.Cleaner

    在 Java 中 NIO 直接缓冲区分配的内存通过以下方式释放 sun misc Cleaner实例 一些比对象终结更有效的特殊幻像引用 这种清洁器机制是否仅针对直接缓冲区子类硬编码在 JVM 中 或者是否也可以在自定义组件中使用清洁器 例
  • 将多模块 Maven 项目导入 Eclipse 时出现问题 (STS 2.5.2)

    我刚刚花了最后一个小时查看 Stackoverflow com 上的线程 尝试将 Maven 项目导入到 Spring ToolSuite 2 5 2 中 Maven 项目有多个模块 当我使用 STS 中的 Import 向导导入项目时 所
  • Java - 不要用 bufferedwriter 覆盖

    我有一个程序可以将人员添加到数组列表中 我想做的是将这些人也添加到文本文件中 但程序会覆盖第一行 因此这些人会被删除 如何告诉编译器在下一个空闲行写入 import java io import java util import javax
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour
  • javax.persistence.Table.indexes()[Ljavax/persistence/Index 中的 NoSuchMethodError

    我有一个 Play Framework 应用程序 并且我was使用 Hibernate 4 2 5 Final 通过 Maven 依赖项管理器检索 我决定升级到 Hibernate 4 3 0 Final 成功重新编译我的应用程序并运行它

随机推荐

  • URI和URL、URN的作用和区别

    前言 我们都知道URL是使用Web浏览器等访问Web页面时需要输入的网页地址 而对URI URN的认识可能很少 更有甚者会像我一样 把URI与URL搞混 还以为是一个东西的不同别名 其实URI是URL与URN的超集 URI包括URL和URN
  • htons()是什么

    一个数字0x12345678 左边是高字节 右边是低字节 存储到内存中有两种方式 小端法就是把低字节先存入 内存地址里从低到高就会变成 78 56 34 12 相反大端法就是把高字节先存入 内存地址里从低到高存入的数据旧变成了12 34 5
  • ASP连接Excel的方法

    在ASP中 可以将Excel作为一种轻量级数据库 用于存取数据 下面是一个使用ASP连接Excel的代码实例 首先创建excel数据链接 然后打开连接 在excel中名为 招聘 的表格中查询id 1的一条数据 并打印StuName列的数据
  • 操作系统笔记整理3——进程的描述与控制(2)

    点此链接可跳转到 操作系统笔记整理 目录索引页 参考书籍 计算机操作系统 第四版 汤小丹等编著 文章目录 点此链接可跳转到 操作系统笔记整理 目录索引页 线程的概念 线程的运行状态 多线程中的进程 线程的实现 内核支持线程KST 用户级线程
  • 在Surface Pro 4上安装Bliss OS

    安装Bliss OS 起因 为了让已经有点跑不动的苏婆4发挥余热 其实是想玩Arcaea 打算往上面安装安卓系统 之所以不使用模拟器 就是因为模拟器跑起来太慢了 而且像是mumu之类的模拟器还不支持多点触控 失败的尝试 凤凰系统 很漂亮 但
  • websocket中stompjs订阅消息队列消息,无法正常关闭socket带来的浏览器开销问题

    先说一下问题在公司的业务场景 前端页面作为消费者 监听的是rabbitmq中的一个交换机 由此来订阅消息 原生websocket因为无法更好实现监听和数据传输 所以采用stomp来更好创建socket 但是websocket经历一段时间会自
  • 项目启动卡在了Started Application in 10.266 seconds (JVM running for 13.033)

    好端端的项目启动后卡在这一行Started Application in 10 266 seconds JVM running for 13 033 日志中原本打印的执行的banner和程序都没有执行 访问那页面是404 很奇怪 因为啥东西
  • webpack 设置ttf 字体 不报错但是不生效

    webpack config js const path require path module exports entry src index js mode development output filename bundle js p
  • Gitee注册教程

    Gitee注册教程 目录 一 关于Gitee 二 注册Gitee 三 使用Gitee 一 关于Gitee Gitee也叫码云 是开源中国 OSChina 推出的基于Git的代码托管服务 Gitee包括三个版本 分别是 社区版 企业版和高校版
  • prometheus:(二)监控概述

    目录 一 监控系统概论 运维监控平台设计思路 二 prometheus基础资源监控 2 1网络监控 2 2存储监控 2 3服务器监控 2 4中间件监控 2 5应用程序监控 APM 三 常用监控系统介绍 3 1 Cacti 3 2 Nagio
  • tensorflow之Optimizers(tensorflow的优化器)

    一 概述 1 默认情况下 优化器训练目标函数所依赖的所有可训练变量 如果你不想训练某一个变量 你可以将关键词trainable设置为False 举例如下 global step tf Variable 0 trainable False d
  • 图像语义分割方法研究进展

    全监督学习的图像语义分割方法研究进展 简介 1 全监督学习的图像语义分割方法 1 1 基于全卷积的图像语义分割方法 1 2 基于编码器解码器结构的图像语义分割方法 1 3 基于注意力机制的图像语义分割方法 1 4 基于添加特殊模块的图像语义
  • nginx: configuration file /home/xx.local/etc/nginx/nginx.conf test failed

    nginx启动失败 输入 nginx t c HOME local etc nginx nginx conf nginx alert could not open error log file open var log nginx erro
  • 提升代码质量的几点建议

    在我从事编程工作的过程中 提升代码质量是一个极其重要且不可忽视的问题 在我看来 提升代码质量需要注意以下几点 1 遵守规范 包括变量命名规范 文件命名规范 方法命名规范等等 2 保持代码简洁 我们应该尽量避免冗余 复杂的代码 使用有意义的变
  • A - C语言实验——求一个3*3矩阵对角线元素之和

    Description 给定一个3 3的矩阵 请你求出对角线元素之和 Input 按照行优先顺序输入一个3 3矩阵 每个矩阵元素均为整数 Output 从左下角到右上角这条对角线上的元素之和 Sample Input 1 2 3 3 4 5
  • [MRCTF2020]千层套路1

    BUU题目复现 开局一个压缩包 flag全靠懵 拿到压缩包第一件事直接看能不能直接解压缩 很明显 有密码 不行 下一步 使用010Editor查看内部结构 发现确实操作着很多的 zip 文件 但是我使用binwalk foremost都没办
  • Python爬虫实战,requests模块,Python实现IMDB电影top数据可视化

    前言 利用Python爬取IMDB电影 废话不多说 让我们愉快地开始吧 开发工具 Python版本 3 6 4 相关模块 requests模块 random模块 bs4模块 以及一些Python自带的模块 环境搭建 安装Python并添加到
  • 凯恩帝对刀和刀补_KND数控车床试切对刀和调刀补

    展开全部 M 是测量的意思 有的系统是输32313133353236313431303231363533e78988e69d8331333262353363入你的测量值 然后点一个 测量 按钮 数控车对刀步骤 第一把刀的对刀步骤 第一步 确
  • linux 查询服务器的配置信息

    linux下看配置 可没有windows那么直观 你只能一个一个查看 一 cpu root srv more proc cpuinfo grep model name root srv grep model name proc cpuinf
  • 【Java数据结构】泛型详解+图文,通配符上界、下界

    0 泛型的本质 0 泛型的目的 1 泛型的语法 1 1 泛型的使用 2 包装类 2 1 装箱和拆箱 2 2 1练习题 3 泛型如何编译 4 泛型的上界 5 通配符 5 1通配符上界 5 2通配符下界 有坑填坑 0 泛型的本质 泛型的本质 泛