[Java]泛型

news2024/12/26 11:09:43

文章目录

  • 🥽 泛型简介
    • 🌊 泛型的概念
    • 🌊 使用泛型的原因
    • 🌊 小结
    • 🌊 类型推断
  • 🥽 自定义泛型
    • 🌊 自定义泛型类
    • 🌊 自定义泛型类/泛型接口注意点
    • 🌊 自定义泛型方法
  • 🥽 泛型在继承上的体现
  • 🥽 通配符的使用
  • 🥽 有限制的通配符


🥽 泛型简介

🌊 泛型的概念

JDK1.5以后,Java引入了“参数化类型(Parameterized type)”(泛型)的概念,允许我们在创建集合时再指定集合元素的类型。JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时指定元素的类型。

泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参

🌊 使用泛型的原因

不使用泛型可能出现的问题:

public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        // 需求:存放学生成绩
        list.add(89);
        list.add(88);
        list.add(87);
        list.add(86);
        // 问题1:类型不安全,可以存放不同类型的元素
        list.add("TOM");
        for (Object score: list ) {
            // 问题2:强转时,可能出现类型转换异常
            int stuScore = (int)score;
            System.out.println(stuScore);
        }
    }

在这里插入图片描述

使用泛型:

泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换。

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        // 需求:存放学生成绩
        list.add(89);
        list.add(88);
        list.add(87);
        list.add(86);
        // 编译时,会进行类型的检查,保证数据的安全
        // list.add("TOM");
        // 获取时,获取的数据为使用泛型指定元素类型
        for (Integer score: list ) {
            // 避免了强转操作
            int stuScore = score;
            System.out.println(stuScore);
        }
    }

在这里插入图片描述

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁(可以不用强转)、健壮。

🌊 小结

① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
② 在实例化集合类时,可以指明具体的泛型类型
③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。比如:add(E e) —>实例化以后:add(Integer e)
④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。

🌊 类型推断

// ArrayList<Integer> list = new ArrayList<Integer>();
// 如果在变量前面指明了泛型的类型,则在new的后面可以不用再写泛型的类型
// jdk7新特性:类型推断
ArrayList<Integer> list = new ArrayList<>();

🥽 自定义泛型

🌊 自定义泛型类

public class Order<T> {
    int orderId;
    String orderName;
    // 给自定义类加上泛型后,
    // 在类的内部结构就可以使用类的泛型
    // 某个属性的类型为T,属性类型T由实例化类时指定
    T orderT;

    public Order() {}

    public Order(int orderId, String orderName, T orderT) {
        this.orderId = orderId;
        this.orderName = orderName;
        this.orderT = orderT;
    }

    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }
}
    public static void main(String[] args) {
        // 如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object
        // 如果定义了泛型,建议在实例化时指明类的泛型
        Order order = new Order();
        order.setOrderT(123);
        order.setOrderT("Tom");
        // 建议:实例化时指明类的泛型
        Order<String> order1 = new Order<>();
        // 报错
        // order1.setOrderT(123);
        order1.setOrderT("Order1");
    }

定义泛型类的子类,并且指明泛型的类型:

// SubOrder1 指明了泛型类型,不为泛型类
public class SubOrder extends Order<Integer> {
    public SubOrder() {}
}
    public static void main(String[] args) {
        SubOrder sub1 = new SubOrder();
        // 由于子类在继承带泛型的父类时,指明了泛型类型
        // 实例化时不再需要指明泛型
        // 且在子类中用到泛型的位置都为继承时指定的泛型类型
        sub1.setOrderT(123);
    }

定义泛型类的子类,不指明泛型的类型:

// SubOrder1<T> 没有指定泛型类型,仍然为泛型类
public class SubOrder1<T> extends Order<T> {
    public SubOrder1() {}
}
    public static void main(String[] args) {
        SubOrder1<String> sub1 = new SubOrder1();
        sub1.setOrderT("ABC");
    }

🌊 自定义泛型类/泛型接口注意点

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内,使用逗号进行分隔。比如:<E1,E2,E3>

  2. 泛型类的构造器如下:public GenericClass(){}。而下面是错误的:public GenericClass<E>(){}。声明泛型类的构造器不需要写<E>

  3. 实例化后,使用到泛型的位置的类型都为指定的泛型类型,没有指定则为Object。

  4. 泛型不同的引用(泛型类不同的实例化对象)之间不能相互赋值。

  5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。泛型要使用一路都用。要不用,一路都不要用。

  6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象(接口或抽象类不能实例化)。

  7. jdk1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();

  8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。

  9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。类的泛型是在实例化时进行指定,而静态结构在类加载时就被加载,静态结构的使用早于类的实例化。

  10. 异常类不能是泛型的

  11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];

        public void test() {
            // 编译不通过
            // 此时new需要一个具体的类型,而T不确定
            // T[] arr = new T[];
            // 编译通过,Object为具体的类型
            // 运行时运行到此处会将其强转为T类型
            T[] arr = (T[])new Object[10];
        }
    
  12. 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

    • 子类不保留父类的泛型:按需实现
      • 没有类型 擦除
      • 具体类型
    • 子类保留父类的泛型:泛型子类
      • 全部保留
      • 部分保留
    class Father<T1, T2> {
    }
    // 子类不保留父类的泛型
    // 1)没有类型 擦除
    class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{
    }
    // 2)具体类型
    class Son2 extends Father<Integer, String> {
    }
    // 子类保留父类的泛型
    // 1)全部保留
    class Son3<T1, T2> extends Father<T1, T2> {
    }
    // 2)部分保留
    class Son4<T2> extends Father<Integer, T2> {
    }
    
    class Father<T1, T2> {
    }
    // 子类不保留父类的泛型
    // 1)没有类型 擦除
    // A B 为子类额外提供的泛型,从父类继承的泛型都为Object类型,在此基础上子类多了 A B 两个泛型
    // A B 属于子类
    class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
    }
    // 2)具体类型
    // 从父类继承的泛型为Integer, String类型,在此基础上子类多了 A B 两个泛型
    // A B 属于子类
    class Son2<A, B> extends Father<Integer, String> {
    }
    // 子类保留父类的泛型
    // 1)全部保留
    // 保留父类的全部泛型,子类又额外定义了 A B 泛型
    class Son3<T1, T2, A, B> extends Father<T1, T2> {
    }
    // 2)部分保留
    // 保留父类的T2泛型,子类又额外定义了 A B 泛型
    class Son4<T2, A, B> extends Father<Integer, T2> {
    }
    

    结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型

🌊 自定义泛型方法

泛型方法:在方法的结构中出现了泛型的结构,且方法的泛型和类的泛型没有任何关系,即类的泛型参数与方法的泛型参数两个不同的独立的参数,泛型方法与所属的类是否为泛型类没有关系。

    public static void main(String[] args) {
        _1 v = new _1();
        Integer[] arr = new Integer[]{1, 2, 3, 4};
        // 泛型方法在调用时指明泛型参数的类型
        // 泛型方法的泛型类型与泛型类的泛型类型无关
        List<Integer> list = v.test(arr);
    }

    // <E> 表明后面使用的E为泛型
    // 否则E会被认为类进行处理 public List<E> test(E[] arr)
    public <E> List<E> test(E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e: arr) {
            list.add(e);
        }
        return list;
    }

泛型方法可以声明为静态的,因为泛型方法的泛型参数是在调用方法时确定的,并非在实例化时确定。

    public static void main(String[] args) {
        Integer[] arr = new Integer[]{1, 2, 3, 4};
        // 泛型方法在调用时指明泛型参数的类型
        // 泛型方法的泛型类型与泛型类的泛型类型无关
        List<Integer> list = test(arr);
    }

    // <E> 表明后面使用的E为泛型
    // 否则E会被认为类进行处理 public List<E> test(E[] arr)
    public static <E> List<E> test(E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e: arr) {
            list.add(e);
        }
        return list;
    }

🥽 泛型在继承上的体现

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!比如:String是Object的子类,但是List<String>并不是List<Object>的子类。

在这里插入图片描述

    public void test1() {
        List<Object> l1 = new ArrayList<>();
        List<String> l2 = new ArrayList<>();
        // 如果 l1 = l2
        // 则可以通过 l1 = l2, l1.add(123)向l2中加入不为String类型的数据
    }

如果B是A的一个子类型,且A与B是具有泛型声明的类或接口,B<G>A<G>的子类型

    public void test2() {
        List<String> l1 = null;
        ArrayList<String> l2 = null;
        l1 = l2;
    }

🥽 通配符的使用

类型通配符:?

比如:List<?> Map<?,?>List<?>List<String>List<Object>等各种泛型List的父类。

如果B是A的一个子类型,G<A>G<B>没有关系,二者共同的父类为G<?>

    public void test3() {
        List<String> l1 = new ArrayList<>();
        List<Integer> l2 = new ArrayList<>();
        print(l1);
        print(l2);
    }
    
    public void print(List<?> l) {
        l.forEach(System.out::println);
    }

对于List<?>不能向其内部添加数据,因为我们不知道需要向其中添加的元素类型,不能向其中添加对象。唯一的例外是null,它是所有类型的成员。

读取List<?>的对象中的元素时,永远是安全的,因为不管List<?>对象中包含的真实类型是什么,都可以赋值给Object。

🥽 有限制的通配符

  • <?>
    • 允许所有泛型的引用调用
  • 通配符指定上限
    • 上限extends:泛型使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
  • 通配符指定下限
    • 下限super:泛型使用时指定的类型不能小于操作的类,即>=
  • 举例:
    • <? extends Number> (无穷小 , Number]
      • 只允许泛型为Number及Number子类的引用调用
    • <? super Number> [Number , 无穷大)
      • 只允许泛型为Number及Number父类的引用调用
    • <? extends Comparable>
      • 只允许泛型为实现Comparable接口的实现类的引用调用
	// Student为Person的子类
    @Test
    public void test4(){

        List<? extends Person> list1 = null;
        List<? super Person> list2 = null;

        List<Student> list3 = new ArrayList<Student>();
        List<Person> list4 = new ArrayList<Person>();
        List<Object> list5 = new ArrayList<Object>();

        list1 = list3;
        list1 = list4;
//        list1 = list5; // Object的范围大于Person

//        list2 = list3; // Student的范围小于Person
        list2 = list4;
        list2 = list5;

        //读取数据:
        list1 = list3;
        Person p = list1.get(0); // 最大不超过Person
        //编译不通过
        //Student s = list1.get(0);  // Person的子类不一定都为Student 

        list2 = list4;
        Object obj = list2.get(0); // 最小不低于Person
        编译不通过
//        Person obj = list2.get(0); // 都为Person的父类或Person类

        //写入数据:
        // 注意允许的范围
		// 子类可以放进去,子类可以赋值给父类,范围小的可以赋值给范围大的

        //编译不通过
        // 可能存在比Student范围还小的类,父类对象赋值给子类
//        list1.add(new Student());

        //编译通过
        list2.add(new Person());
        list2.add(new Student());

    }

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

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

相关文章

2022HW11

文章目录任务描述-域自适应二、代码三、实验1、Simple Baseline2、Medium Baseline3、Strong Baseline任务描述-域自适应 ●想象一下&#xff0c;你想做与3D环境相关的任务&#xff0c;然后发现 ○3D图像很难标记&#xff0c;因此也很昂贵。 ○模拟图像&#xff08;如GTA-5上的…

shell-流程控制之循环

1.for创建20用户用户前缀由用户输入用户初始密码由用户输入 例如&#xff1a;test01,test10 [rootcotenos day06]# vim useradd.sh #!/bin/bash read -p "请输入用户前缀&#xff1a;" user read -p "请输入初始密码&#xff1a;" pass for ((i1;i<…

重新成长再出发

文章大纲突然的转型&#xff0c;逼自己一把也能重新成长为什么脱口秀突然火了起来&#xff1f;上天是给了你一个重新成长的机会&#xff01;2022 一些成长的点滴突然的转型&#xff0c;逼自己一把也能重新成长 年初的时候&#xff0c;航旅纵横给我弹窗问我很久没有坐飞机了怎么…

微三云陈志坤:盘点帮助企业转型到线上的模式

今/天小编给大家介绍一下泰山众筹模式系统&#xff0c;想搭建系统平台的联系微三云陈志坤。 泰山众筹模式已经走了4个多年头&#xff0c;目前仍在运行。 第一部分&#xff1a;泰山众筹模式介绍 一、无泡沫——安全长久 所有的互联网金融项目的死穴就是泡沫无法消除&#xf…

对于此版本,windows installer和即点即用的Office不能并行运行,因此只能安装一种类型(安装Visio破解版时的报错)

阅读前请看一下&#xff1a;我是一个热衷于记录的人&#xff0c;每次写博客会反复研读&#xff0c;尽量不断提升博客质量。文章设置为仅粉丝可见&#xff0c;是因为写博客确实花了不少精力。不用担心你关注我而我却不关注你&#xff0c;因为我是个诚信互关的人&#xff01;&…

gem5 arm架构 fullsystem spec2017 benchmark 仿真

gem5 system emulation 模式&#xff0c;内部实现了对system call的模拟&#xff0c;使用了一段时间后&#xff0c;有一些发现: 如果使用spec2017 X86编译&#xff0c;那么会存在对intel比较新的指令不支持的问题&#xff1b;后来使用gcc march K6 m32来解决&#xff0c;即使用…

FPGA知识汇集-FPGA配置模式和配置设计

所有现代FPGA的配置分为两类:基于SRAM的和基于非易失性的。其中&#xff0c;前者使用外部存储器来配置FPGA内的SRAM;后者只配置一次。 Lattice和Actel的FPGA使用称为反熔丝的非易失性配置技术&#xff0c;其主要优点是系统设计更加简单、不需要外部存储器和配置控制器、功耗低…

Kotlin之泛型的高级特性

Kotlin泛型中的基本用法和Java中的泛型用法是大致相同的&#xff0c;因此也比较好理解。然而实际上&#xff0c;Kotlin在泛型方面还提供了不少特有的功能&#xff0c;接下来将进行介绍。 一、对泛型进行实化 泛型实化这个功能对于绝大多数Java程序员来讲是非常陌生的&#xf…

对云台、IMU、鲁棒性和硬件时间同步的理解

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> slam是一门集硬件和软件的多科学技术&#xff0c;涉及到很多技术术语、概念以及数学公式等等。下面我将结合网上资料以及个人理解进行介绍。 什么叫云台…

navicat导入sql数据库文件的简单操作步骤

目录 前言必读 一、概念 二、操作步骤 &#xff08;一&#xff09;新建连接 &#xff08;二&#xff09;新建数据库 &#xff08;三&#xff09;数据库导入sql文件 前言必读 读者手册&#xff08;必读&#xff09;_云边的快乐猫的博客-CSDN博客 一、概念 在很多项目当…

LeetCode 309. 最佳买卖股票时机含冷冻期

309. 最佳买卖股票时机含冷冻期 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;: 卖出股票后&#x…

第二个岳云鹏,跨年晚会含泪主持,成为一道最靓丽的风景

中国人自古讲究&#xff1a;百善孝入先&#xff0c;尤其是对于娱乐圈的明星来说&#xff0c;孝心和爱心更是他们成功的根本。 在这方面&#xff0c;德云社的小岳岳走在了前列&#xff0c;他用自己的孝心和爱心感动了粉丝&#xff0c;也收获了无数的鲜花和掌声。小岳岳的爱心体现…

2022年终总结 2023展望

一、回首2022 总结2022&#xff0c;同时也给新的一年设定目标&#xff0c;明年来还愿。 2022对我来说意义非凡&#xff0c;最重要莫过于考上了理想的研究生学校。 1~2月 考研初试刚结束&#xff0c;很长时间没敲过代码&#xff0c;而且本科期间刷的算法题不多&#xff0c;感觉…

02 USART

串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信&#xff0c;极大地扩展了单片机的应用范围&#xff0c;增强了单片…

RabbitMQ——高级特性

1、RabbitMQ高级特性 1.1、消息的可靠性投递 在使用 RabbitMQ 的时候&#xff0c;作为消息发送方希望杜绝任何消息丢失或者投递失败场景。 RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。 ⚫ confirm 确认模式 ⚫ return 退回模式 rabbitmq 整个消息投递的路径…

【回答问题】ChatGPT上线了!推荐30个以上比较好的命名实体识别模型

【回答问题】ChatGPT上线了&#xff01;推荐30个以上比较好的命名实体识别模型以及github源码&#xff1f; 推荐30个以上比较好的命名实体识别模型 命名实体识别模型是指识别文本中提到的特定的人名、地名、机构名等命名实体的模型。推荐的命名实体识别模型有&#xff1a; …

数据仓库搭建——本地数仓搭建详细流程

1 准备 1.1 本地虚拟机器准备 使用本地搭建三台机器&#xff0c;搭建数仓&#xff0c;模拟实际平台数据仓库的搭建。具体的搭建框架如下 安装软件角色主机1主机2主机3HadoopNameNode√SecondaryNameNode√DataNode√√√NodeManager√√√ResourceManager√√Zookeeperzk se…

educoder数据结构与算法 栈 第1关:实现一个顺序存储的栈

本文已收录于专栏 &#x1f332;《educoder数据结构与算法_大耳朵宋宋的博客-CSDN博客》&#x1f332; 目录 任务描述 相关知识 编程要求 测试说明 AC_Code 任务描述 本关任务是实现 step1/SeqStack.cpp 中的SS_IsFull、SS_IsEmpty、SS_Length、SS_Push和SS_Pop五个操作…

巨星大衣哥参加元旦跨年晚会,一首老歌赢得现场一片掌声

刚刚结束的跨年晚会&#xff0c;各地都展现出了极大热情&#xff0c;这是后疫情时代的第一次跨年&#xff0c;自由了的国人自然兴奋异常。从南国到北疆、从湖南到山东&#xff0c;人们都沉浸在喜悦当中&#xff0c;期盼祖国风调雨顺&#xff0c;期盼来年都能身体安康。 远在山东…

【回答问题】ChatGPT上线了!推荐30个以上比较好的中文bert系列的模型/压缩模型

推荐30个以上比较好的中文bert系列的模型 以下是一些中文 BERT 系列的模型: BERT-Base, Chinese: 中文 BERT 基础版 BERT-Large, Chinese: 中文 BERT 大型版 BERT-WWM, Chinese: 中文 BERT 加入了 whole word masking 的版本 BERT-WWM-Ext, Chinese: 中文 BERT 加入了 whole …