8.0 泛型

news2024/11/27 4:28:22

通过之前的学习,读者可以了解到,把一个对象存入集合后,再次取出该对象时,该对象的编译类型就变成了Object类型(尽管其在运行时类型没有改变)。集合设计成这样,提高了它的通用性,但是也带来了一些类型不安全和繁琐的问题,例如,集合可以同时存储任何类型的对象,通常对取出之后的对象都需要强制类型转换,而且如果不知道实际参数类型的情况,也无法进行强制类型转换。为了解决这些问题,从JDK 5版本开始引入了泛型,本章将围绕泛型的相关内容进行讲解。

1. 泛型基础

1.1. 泛型的概念

泛型是在JDK 5中引入的一个新特性,其本质是参数化类型,也就是将具体的类型形参化,参数化的类型(可以称之为类型形参)在使用或者调用时传入具体的类型(类型实参),类似于调用方法时传入实参才确定方法形参的具体值。泛型的声明由一对尖括号和类型形参组成,类型形参定义在尖括号中间,定义类、接口和方法时使用泛型声明,定义出的类、接口和方法分别称为泛型类、泛型接口和泛型方法。

1.2. 泛型的定义

使用泛型编程,会在使用或者调用时传入具体的类型时才确定最终的数据类型,所以集合需要存储什么类型的数据,在创建集合时传入对应的类型即可。

定义泛型时类型形参由一对尖括号(<>)包含在中间,使用或者调用泛型时,需要将类型实参写在尖括号(<>)之间。

JDK 5之后的类库中很多重要的类和接口都引入了泛型,例如集合体系中的类和接口。下面分别演示未引入泛型和使用泛型编程的区别,体验泛型具体有什么好处。

(1)未引入泛型前

public class TestDemo {
    
    @Test
    public void test(){
        // 创建一个只保存Integer类型的List集合
        List intList = new ArrayList();
        intList.add(1);
        intList.add(2);
        //因为失误存放了Integer类型之外的字符串数据
        intList.add("3");
        for (int i = 0; i < intList.size(); i++) {
            /*因为List里面默认取出的全部Object对象,所以使用之前需要进行强
             * 制类型转换。集合内最后一个元素进行转换时候将出现类型转换异常
             * */
            Integer num=(Integer)intList.get(i);
        }
    }
}

(2)引入泛型后

public class TestDemo {
    @Test
    public void test(){
        // 创建一个只保存Integer类型的List集合
        List<Integer> intList = new ArrayList<Integer>();
        intList.add(1);
        intList.add(2);
        //下面代码将出现编译时异常
        intList.add("3");
        for (int i = 0; i < intList.size(); i++) {
            //下面的代码无需强制类型转换
            Integer num=intList.get(i);
        }
    }
}

1.3. 泛型的好处

使用泛型的好处如下:

  • 提高类型的安全性

使用泛型后,将类型的检查从运行期提前到编译期,编译期的类型检查,可以更早、更容易的找出因为类型限制而导致的类型转换异常,从而提高程序的可靠性。

  • 消除强制类型转换

使用泛型后,程序会记住当前的类型形参,从而无需对传入的实参值进行强制类型转换。使得代码更加清晰和筒洁,可读性更高。

  • 提高代码复用性

使用泛型后,可以更好的将程序中通用的代码提取出来,在使用时传入不同类型的参数,避免了多次编写相同功能的代码,以提高代码的复用性。

  • 拥有更高的运行效率

使用泛型之前,传入的实际参数值作为Object类型传递时,需要进行封箱和拆箱操作,会消耗程序的一定的开销。使用泛型后,类型形参中都需要使用引用数据类型,即传入的实际参数的类型都是对应引用数据类型,避免了封箱和拆箱操作,降低了程序运行的开销,提高了程序运行的效率。

2. 泛型类

2.1. 泛型类的语法格式

定义类时,在类名后加上尖括号包含类型形参,定义的这个类就是泛型类。创建泛型类的实例对象时传入不同的类型实参,从而可以动态生成无数个该泛型类的子类。在JDK类包中泛型类的最典型应用就是各种容器类,如ArrayList、HashMap等。定义泛型类的格式具体如下。

public class 类名<类型形参变量>{
    
}

上述语法格式中,类名<类型形参变量>是一个整体的数据类型,通常称为泛型类型;类型形参变量,没有特定的意义,可以是任意一个字母,但是为了提高可读性,建议使用有意义的字母。一般情况下使用较多的字母及意义如下所示。

  • E:表示Element(元素),常用在java Collection里使用,如 List<E>,Iterator<E>,Set<E>。

  • K,V:表示Key,Value(Map的键值对)。

  • N:表示Number(数字)。

  • T:表示Type(类型),如String,Integer等。

2.2. 泛型类的定义与创建

定义:定义泛型类时,类的构造方法名称还是类的名称,类型形参变量可以用于属性的类型、方法的返回值类型和方法的参数类型。

创建:创建泛型类的对象时,不强制要求传入类型实参,如果传入类型实参,类型形参会根据传入的类型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入类型实参的话,在泛型类中使用类型形参的方法或成员变量定义的类型可以为任何的类型。

(1)定义泛型类Goods,声明私有变量info,定义构造方法,与getter/setter方法。

/**
 * 定义泛型类Goods
 * @param <T>
 */
public class Goods<T> {
    // 类型形参变量作用于属性的类型
    private T info ;
    //无参构造方法
    public Goods(){

    }
    // 类型形参变量作用于构造方法的参数类型
    public Goods(T info) {
        this.info = info;
    }
    // 类型形参变量作用于方法的参数类型
    public void setInfo(T info){
        this.info = info ;
    }
    // 类型形参变量作用于方法的返回值类型
    public T getInfo(){
        return this.info ;
    }
}

(2)定义测试类,创建Goods对象,分别调用setInfo()方法和getInfo()方法。

public class TestDemo{
    @Test
    public void test(){
        Goods goods = new Goods();
        goods.setInfo("电脑");
        System.out.println(goods.getInfo() + ":" + goods.getInfo().getClass());
        goods.setInfo(200);
        System.out.println(goods.getInfo() + ":" + goods.getInfo().getClass());
    }
    @Test
    public void test2(){
        Goods<String> goods = new Goods<>();
        goods.setInfo("电脑");
        System.out.println(goods.getInfo() + ":" + goods.getInfo().getClass());
    }
    @Test
    public void test3(){
        Goods<Integer> goods= new Goods<>();
        goods.setInfo(200);
        System.out.println(goods.getInfo() + ":" + goods.getInfo().getClass());
    }
}

2.3. 泛型类的练习

定义一个泛型类Point<T>,其中包含x和y两个类型为T的成员,定义类的带参构造方法,为x和y定义setter和getter,定义show方法输出坐标。

编写测试方法,创建Point<Integer>对象和Point<Double>对象。

  • Point<T>类

public class Point<T> {
    //泛型成员
    private T x;
    private T y;
    //构造方法
    public Point(T x,T y){
        this.x = x;
        this.y = y;
    }

    public T getX() {
        return x;
    }
    public void setX(T x) {
        this.x = x;
    }
    public T getY() {
        return y;
    }
    public void setY(T y) {
        this.y = y;
    }
    //输出坐标
    public void show(){
        System.out.println("x坐标是:" + x + ",y坐标是:" + y);
    }
}
  • 测试类

public class Test {
    public static void main(String[] args) {
        //Integer型
        Point<Integer> p1 = new Point(1,2);
        p1.show();
        //Double型
        Point<Double> p2 = new Point(1.1,2.2);
        p2.show();
    }
}

3. 泛型接口

3.1. 泛型接口的语法格式

定义泛型接口和定义泛型类的语法格式类似,在接口名称后面加上尖括号包含类型形参即可。集合相关的接口中很多接口也都是泛型接口,如Collection、List等。定义泛型接口的基本语法格式如下所示:

public interface 接口名称<类型形参变量>{
    
}

3.2. 泛型接口的应用

泛型接口可以有两种类方式实现,第一种是使用非泛型类实现泛型接口,第二种是使用泛型类实现泛型接口。

(1)使用非泛型类实现泛型接口

当使用非泛型类实现接口时,需要明确接口的泛型类型,也就是需要将类型实参传入到接口中。此时实现类重写接口中使用泛型的地方,都需要将类型形参替换成传入的类型实参,这样可以直接使用泛型接口的类型实参,具体代码如下所示。

  • 定义一个泛型接口

public interface Student<T> {
    public abstract void show(T t);
}
  • 定义泛型接口的实现类,在泛型接口后指定类型实参以明确接口的泛型类型。

public class StudentImpl implements Student<String>{
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}
  • 定义测试类,创建Student对象时,传入的类型实参必须是String类型,否则编译异常。

public class TestDemo {

    @Test
    public void test(){
        Student<String> stu = new StudentImpl();
        stu.show("你好,我是张三");
    }

}

(2)使用泛型类实现泛型接口

当使用泛型类实现泛型接口时,需要将泛型的声明加在实现类中,并且泛型类和泛型接口使用的都是同一个类型形参变量,否则会出现编译异常。具体代码如下所示。

  • 定义一个泛型接口

public interface Student<T> {
    public abstract void show(T t);
}
  • 定义泛型接口的实现类,使用泛型类实现泛型接口

public class StudentImpl<T> implements Student<T> {

    @Override
    public void show(T t) {
        System.out.println(t);
    }
}
  • 定义测试类,创建Student对象时,传入不同的类型实参,并分别调用show()方法进行输出验证。

public class TestDemo {

    @Test
    public void test(){
        Student<String> stu1 = new StudentImpl<>();
        stu1.show("你好,我是张三");
        Student<Integer> stu2 = new StudentImpl<>();
        stu2.show(20);
    }

}

4. 泛型方法

4.1. 泛型方法的语法格式

泛型方法是将类型形参的声明放在修饰符和返回类型之间的方法。在Java程序中,定义泛型方法常用的格式如下所示:

public [static] [final] <类型形参> 返回值类型 方法名 (形式参数列表){
     
}

定义泛型方法注意事项如下所示:

  • 访问权限修饰符(包括private、public、protected)、static和 final都必须写在类型形参列表的前面。

  • 返回值类型必须写在类型形参列表的后面。

  • 泛型方法可以在泛型类中,也可以在普通类中。

  • 泛型类中的任何方法本质上都是泛型方法,所以在实际使用中很少会在泛型类中再用上面的形式来定义泛型方法。

  • 类型形参可以用在方法体中修饰局部变量,也可以修饰方法的返回值。

  • 泛型方法可以是实例方法(没有用static修饰,也叫非静态方法)也可以是静态方法。

4.2. 泛型方法的应用

如果泛型方法是实例方法,则需要使用对象名进行调用;如果泛型方法是静态方法,可以使用类名进行调用,泛型方法的两种使用方式。

  • 方式一

对象名|类名.<类型实参> 方法名(类型实参列表);
  • 方式二

对象名|类名.方法名(类型实参列表);

两种调用泛型方法的差别在于,方法名之前是否显式地指定了类型实参。调用时是否需要显式地指定了类型实参,要根据泛型方法的声明形式,以及调用时编译器能否从实际参数表中获得足够的类型信息决定,如果编译器能够根据实际参数推断出参数类型,就可以不指定类型实参,反之则需要指定类型实参。

4.3. 泛型方法的练习

下面通过一个案例,演示泛型方法的定义与使用,具体代码如下所示。

(1)定义Student类,在类中定义一个静态泛型方法和一个普通泛型方法。

public class Student {
    // 静态泛型方法
    public static <T> void show(T t) {
        System.out.println(t + ":" + t.getClass());
    }
    // 普通泛型方法
    public <T> void study(T t) {
        System.out.println(t + ":" + t.getClass());
    }
}

(2)定义测试方法,调用方法测试结果。

public class TestDemo {
    @Test
    public void test(){
        //静态方法
        Student.show("你好,我是张三");            // 使用方式一调用静态的泛型方法
        Student.<String>show("你好,我是张三");    // 使用方式二调用静态的泛型方法

        //普通方法
        Student stu = new Student();
        stu.study("好好学习");          // 使用方式一调用普通的泛型方法
        stu.<String>study("好好学习");  // 使用方式二调用普通的泛型方法
    }
}

从运行结果可以得出,泛型方法可以在非泛型类中定义,并且在调用泛型方法的时候确定泛型的具体类型 。上述结果中虽然使用方式一和方式二的输出结果一致,但是方式一隐式的传入类型实参,不能直观的查看到调用的方法是泛型方法,不利于代码的阅读和维护,通常建议使用第二种方式调用泛型方法。

5. 类型通配符

类型通配符使用一个问号(?)表示,类型通配符可以匹配任何类型的类型实参。

下面使用一个案例演示类型通配符的使用。

(1)定义泛型类Student,声明私有变量info,定义有参构造方法和getter方法。

/**
 * 定义泛型类Student
 * @param <T>
 */
public class Student<T> {
    private T info;

    public Student(T info) {
        this.info = info;
    }
    public T getInfo() {
        return info;
    }
}

(2)定义测试方法,创建Student对象,分别传入String类型和Integer类型的类型实参,进行测试。

public class TestDemo {
    @Test
    public void test(){
        // 创建student对象,传入String类型的类型实参
        Student<?> student = new Student<String>("张三");
        System.out.println( student.getInfo()+":"+student.getInfo().getClass());
        // 创建student对象,传入Integer类型的类型实参
        student =new Student<Integer>(20);
        System.out.println( student.getInfo()+":"+student.getInfo().getClass());
    }
}
  • 不使用通配符的情况一

如果创建Student对象时,不使用类型通配符,而是使用指定的类型实参,会出现编译异常,具体如下图所示。

  • 不使用通配符的情况二

使用Object代替类型通配符?接收所有的类型,也会出现编译异常,具体如下图所示。

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

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

相关文章

Java 基础学习(一)Java环境搭建和基本数据类型

1 Java 开发环境搭建 1.1 Java 编程语言 1.1.1 什么是Java编程语言 语言是人类进行沟通交流的各种表达符号&#xff0c;方便人与人之间进行沟通与信息交换&#xff1b;而计算机编程语言则是人与计算机之间进行信息交流沟通的一种特殊语言&#xff0c;也有语法规则、字符、符…

嵌入式八股 | 笔试面试 | 校招秋招 | 题目精选

嵌入式八股精华版1.0所有216道题目如下&#xff1a; 欢迎关注微信公众号【赛博二哈】并加入嵌入式求职交流群。提供简历模板、学习路线、岗位整理等 欢迎加入知识星球【嵌入式求职星球】获取完整嵌入式八股。 提供简历修改、项目推荐、求职规划答疑。另有各城市、公…

vue3使用TinyMCE富文本

TinyMCE 介绍 TinyMCE 是一个功能强大的富文本编辑器&#xff0c;它允许您在网页应用程序中创建和编辑具有丰富格式的内容。官网 github项目地址 文档地址 下载tinymce文件 从网页下载最新版zip&#xff0c;也可以打开下面链接下载。 打开网页 tinymce.zip zh-Hans 将下载…

什么是好的FPGA编码风格?(3)--尽量不要使用锁存器Latch

前言 在FPGA设计中&#xff0c;几乎没人会主动使用锁存器Latch&#xff0c;但有时候不知不觉中你的设计莫名其妙地就生成了一堆Latch&#xff0c;而这些Latch可能会给你带来巨大的麻烦。 什么是锁存器Latch&#xff1f; Latch&#xff0c;锁存器&#xff0c;一种可以存储电路…

【yolov5人行道-斑马线目标检测】

yolov5人行道-斑马线目标检测 数据集yolov5人行道-斑马线目标检测检测模型 数据集 YOLOv5是一种目标检测算法&#xff0c;可以用于检测图像中的人行道-斑马线。在目标检测领域&#xff0c;YOLOv5通过结合多种技术手段&#xff0c;包括使用Mosaic数据增强操作、自适应锚框计算与…

Blender学习笔记:做一个小车

文章目录 轮廓车窗轮胎和车灯 教程地址&#xff1a;八个案例教程带你从0到1入门blender【已完结】 轮廓 1 创建立方体&#xff0c;将其拉伸成长方体。Tab进入编辑模式&#xff1b;CtrlR添加一个纵向的循环边&#xff1b;3进入面模式&#xff1b;E选中后上方的面向上拉伸&…

直播自动互动发言机器人,成功分享与技术实现思路

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 一、引言 在当今的互联网时代&#xff0c;直播间已经成为了一个重要的营销和交流平台。为了提高直播间的关注度和人气&#xff0c;我们需要在直播过程中注重提高停留和增加人气。本…

Portraiture全新4.1.2版本升级更新

关于PS修图插件&#xff0c;相信大家都有安装过使用过&#xff0c;而且还不止安装了一款&#xff0c;比如最为经典的DR5.0人像精修插件&#xff0c;Retouch4me11合1插件&#xff0c;Portraiture磨皮插件&#xff0c;这些都是人像精修插件中的领跑者。其中 Portraiture 刚刚升级…

【第三节:微信小程序 3、app.js配置】微信小程序入门,以思维导图的方式展开3

目录 提供了2个函数&#xff1a; app.js配置 【第三节&#xff1a;微信小程序 3、app.js配置】微信小程序入门&#xff0c;以思维导图的方式展开3 提供了2个函数&#xff1a; app() getApp() --------------------------- app.js配置 App() 功能 Ap…

(附源码)springboot电影售票系统小程序 计算机毕设36991

目 录 摘要 1 绪论 1.1课题目的与意义 1.2研究背景 1.3论文结构与章节安排 1.4小程序框架以及目录结构介绍 2 springboot电影售票系统小程序系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统流程分析 2.2.1 数据…

OpenCV项目开发实战--基本图像分割图生成器

欢迎回到我们有关 OpenCV 的系列文章以及我们如何利用其强大的图像预处理功能。在我们之前的文章的基础上,今天我们向您展示如何创建基本的图像分割图生成器。 具体来说,我们的图像掩模应该帮助识别每个像素是否: 背景的一部分(指定值为0)在感兴趣的对象的边缘(指定值 …

Zookeeper分布式锁实现Curator十一问

前面我们通过Redis分布式锁实现Redisson 15问文章剖析了Redisson的源码&#xff0c;理清了Redisson是如何实现的分布式锁和一些其它的特性。这篇文章就来接着剖析Zookeeper分布式锁的实现框架Curator的源码&#xff0c;看看Curator是如何实现Zookeeper分布式锁的&#xff0c;以…

Ceph----CephFS文件系统的使用:详细实践过程实战版

CephFS 介绍 是一个基于 ceph 集群 且兼容 POSIX 标准的文件系统。 创建 cephfs 文件系统时 需要在 ceph 集群中添加 mds 服务&#xff0c;该服务 负责处理 POSIX 文件系统中的 metadata 部分&#xff0c; 实际的数据部分交由 ceph 集群中的 OSD 处理。 cephfs 支持以内核模块…

计算机服务器中了mallox勒索病毒如何处理,mallox勒索病毒解密文件恢复

科技技术的发展推动了企业的生产运营&#xff0c;网络技术的不断应用&#xff0c;极大地方便了企业日常生产生活&#xff0c;但网络毕竟是一把双刃剑&#xff0c;网络安全威胁一直存在&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机…

2023年【起重机司机(限桥式起重机)】试题及解析及起重机司机(限桥式起重机)复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机司机(限桥式起重机)试题及解析根据新起重机司机(限桥式起重机)考试大纲要求&#xff0c;安全生产模拟考试一点通将起重机司机(限桥式起重机)模拟考试试题进行汇编&#xff0c;组成一套起重机司机(限桥式起重机)…

【Ambari】HDP单机自动化安装(基础环境和MySQL脚本一键安装)

&#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&am…

基于Java SSM框架+Vue实现大学生兼职信息网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现大学生兼职信息网站演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认…

基于PLC的果园灌溉系统设计(论文+源码)

1.系统设计 系统示意图如图2-1所示。某一果园 共有3个灌溉区域&#xff0c;分别为灌溉1#区&#xff0c;灌溉2#区&#xff0c;灌溉3#区&#xff0c;分别使用不同湿度传感器检测湿度&#xff0c;用于各区域控制湿度&#xff0c;进行灌溉&#xff0c;使用相应的灌溉阀进行灌溉。这…

Deep Learning(wu--46)

文章目录 ContentsBeginBasic逻辑回归SGD导数计算图&#xff08;反向传播&#xff09;向量化广播numpy Neural Network向量化激活函数梯度下降深层表示反向传播 Contents Begin Basic 逻辑回归 SGD 导数 计算图&#xff08;反向传播&#xff09; 向量化 广播 numpy Neural Netw…

Matplotlib不规则子图_Python数据分析与可视化

除了网格子图&#xff0c;matplotlib还支持不规则的多行多列子图网格。 plt.GridSpec()对象本事不能直接创建一个图形&#xff0c;他只是 plt.subplot()命令可以识别的简易接口。 这里创建了一个带行列间距的23网格&#xff1a; grid plt.GridSpec(2, 3, wspace0.4, hspace0…