Java泛型的使用和原理

news2024/11/16 5:56:40

文章目录

      • 泛型-概述
      • 基础使用
        • 泛型类的使用
        • 泛型类派生子类
        • 泛型接口
        • 泛型方法
      • 类型通配符
        • 类型通配符上限
        • 类型通配符下限
      • 常用泛型标识符
      • 类型擦除
      • 使用注意
        • 泛型与数组
        • 泛型和反射
      • 其他

泛型-概述

Java 泛型(generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是将类型进行参数化,也就是说所操作的数据类型被指定为一个参数。编译时根据泛型参数指定的类型完成检查和转换为Object。

为什么需要泛型?

  • 编译期类型检查,避免类转换异常
  • 减少类型抽象情况下的数据类型转换编码工作

JDK1.5之前没有泛型,以前使用Object或公共基础父类完成泛型功能,但是这种方式在静态代码层面是无法检查它的正确性的,发现不了转换错误,引入泛型是程序的转换错误能在编译阶段被发现。(但如果用反射的话是在运行时做转换的,无法完成检查)。

基础使用

泛型类的使用

  • 定义泛型类

    calss 类名称 <泛型标识, 泛型标识, ……> {
        private 泛型标识 变量名;
    }
    
  • 使用泛型类

    类名<具体的数据类型> 对象名 = new 类名<>();
    
  • 使用注意:

    • 在没有指定具体数据类型时,将按照Object类型来操作
    • 不支持基本数据类型(因为泛型实质是将对象转换为Object类型,基本数据类型不继承自Object无法完成转换)
    • 同一泛型类根据不同数据类型创建的对象,本质是同一个类型(Class对象是同一个)

泛型类派生子类

从泛型类派生子类时,需遵守如下规则:

  • 子类也是泛型类时,可将子类的泛型标识指定给父类表示父类泛型使用和子类泛型一样的数据类型,如 class ChildGeneric<T> extends Generic<T>

  • 子类不是泛型时,要在继承父类时明确指定父类泛型的数据类型

    class ChildGeneric extends Generic<String>

  • 如果不给父类指定泛型类型,此时父类泛型的数据类型为Object。(不推荐使用这样做),如 class ChildGeneric<T> extends Genericclass ChildGeneric extends Generic

泛型接口

  • 定义方式和泛型类一样
  • 使用方式和派生子类一样

泛型方法

定义泛型方法:

修饰符 <T, E, ……> 返回值类型 方法名(形参列表) {
    方法体……
}

使用泛型方法:泛型方法的泛型类型是通过调用方法时指定的

使用注意:

  • 方法的泛型标识可以和类的泛型标识一样但使用时完全没有关系,独立指定。

  • 方法的泛型必须在形参中体现,否则在调用的地方无法指定泛型的类型

  • static的使用:泛型类的泛型给类方法使用时,不能给类方法加 static 修饰符;泛型方法的泛型给方法使用时则可以加 static 修饰符。原因是泛型类的泛型必须在创建对象时指定,使用static方法时无法指定泛型的实际数据类型,而泛型方法的泛型类型是在使用方法时指定的。

  • 泛型可以搭配可变参数使用(参数的数据类型必须相同,至少要有公共父类),如:

    public static <E> void print(E... e) {
        for(int i = 0; i < e.length; i++) {
            System.out.println(e[i]);
        }
    }
    

类型通配符

在使用泛型类/接口/方法修饰变量时必须指定泛型的数据类型,但如果是方法的形成且还不能确定,可以使用 ? 代替具体的类型实参。如:

public void test(ArrayList<?> list) {
    list.add(new Person()); // 这里会编译报错
}

类型通配符上限

定义类型通配符上限语法: 泛型类/泛型接口<? extend 实参类型>

作用:使可以通过泛型调用继承的实参类型的方法(因为通配符上限限定了传入的数据一定会是实参类型或其子类型本身)

上限的使用限定:

  • 可以限定传递的类型必须是实参类型(语法中extend后面的数据类型)或其子类类型

  • 使用通配符上限修饰的变量时是不允许做复制操作或者对泛型集合做添加操作的(因为无法确定传进来的到底是什么类型的数据,即使添加实参类型的数据也不行),如下面写法是错误的

    public void test(ArrayList<? extends Person> list) {
        list.add(new Person()); // 这里会编译报错
    }
    

类型通配符下限

定义类型通配符下限语法: 泛型类/泛型接口<? super 实参类型>

下限的使用限定:

  • 具体使用时传入的类型必须是实参类型(语法中super后面的数据类型)或实参类型的父类类型
  • 使用通配符下限修饰的变量可以填充元素,但是只能填充实参类型和其子类类型
  • 使用通配符下限修饰的变量在方法中使用时,类型都是Object

使用案例:JDK中使用sort方法时可以传入一个 Comparator 比较器,Comparator 就使用了通配符且使用的是通配符下限,

常用泛型标识符

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(Java 类)
  • K - Key(键)
  • V - Value(值)
  • N - Number(数值类型)
  • ? - 表示不确定的 java 类型

类型擦除

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的。但是泛型代码能够很好地和之前版本的代码兼容,那是因为泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为类型擦除

类型擦除后的代码:

  • 无限定的泛型:擦除为Object类型

    image-20221203120356374

  • 有上限的泛型:擦除为上限类型

    image-20221203120417956

桥接方法:当实现接口时必须实现接口中的方法,泛型接口的方法使用到泛型编译后泛型会转换为 Object ,而如下图中的 InfoImp 在实现 Info<Integer> 接口时重写方法时泛型变为了具体的 Integer , InfoImp 编译后和 Info 对不上(因为 InfoImpl 没有重写 Object info(Object var) ,而是重写的 Integer info(Integer var) ,为了解决这个问题,引入桥接方法保持泛型接口和其子类的关系

image-20221203120127181

使用注意

泛型与数组

泛型数组的使用限制规则:可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象。如下写法会编译报错:Pair<String>[] p=new Pair<String>[10];

为什么不能直接创建带泛型的数组对象?

数组必须在定义时就知道它的所有元素的数据类型且检查所有元素的数据类型都是一致的,而泛型具有擦除机制,每个元素的类型需要到运行时使用时才具体确定,两者的特性冲突。

如何曲线救国?

  • 尽量不要使用泛型数组,可以使用泛型集合代替

  • 对间接依赖的泛型可以不指定,如 List<String>[] list = new List[3]

  • 对直接依赖泛型的数组(如T[]数组)可以通过java.lang.reflect.Array的newInstance(Class<T>, int)创建

泛型和反射

  • 反射中主要用到 Class<T>Constructor<T> 两个泛型类。如:

    Class<Person> personClass = Person.class;
    Construct<Person> construct = personClass.getConstructor();
    Person person = construct.newInstance();
    
  • Java的泛型是“伪泛型”,只在编译期间做类型检查,当搭配反射使用进行运行时类型擦除可能导致实际的类型并不是定义的类型。如:

    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
    
        List<Integer> intlist = list;
    
        for (int i = 0; i < intlist.size(); i++) {
            // 此时报错,转换异常
            System.out.println(intlist.get(i)*100);
        }
    }
    

其他

实参和形参的区别

实参和形参

参考资料:
黑马程序员——JavaSE强化教程泛型,由点到面的讲解了整个泛型体系

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

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

相关文章

第十四届蓝桥杯集训——JavaC组第八篇——进制转换

第十四届蓝桥杯集训——JavaC组第八篇——进制转换 目录 第十四届蓝桥杯集训——JavaC组第八篇——进制转换 短除法 十进制转二进制示例&#xff1a; 十进制转换二进制 十进制转换八进制 十进制转换十六进制 二进制转十进制 八进制转十进制 十六进制转十进制 进制转换…

【✨十五天搞定电工基础】半导体器件

本章要求1. 理解PN结的单向导电性&#xff0c;三极管的电流分配和电流放大作用 2. 了解二极管、稳压管和三极管的基本构造、工作原理和特性曲线&#xff0c;理解主要参数的意义 3. 会分析含有二极管的电路 目录 一、半导体基础知识 1、本征半导体的导电机理 2、杂质半导体 …

部分核心技术(持续更新)

文章目录1.Schedule&#xff08;定时任务&#xff09;2.高并发线程安全的解决方案2.1为什么不适用同步锁&#xff08;Synchronized&#xff09;&#xff1f;2.2 Redis的分布式锁setnx2.3 redisson分布式锁&#xff08;看门狗机制&#xff09;2.3.1 Redis的分布式锁setnx产生的问…

保姆级入门nest笔记

使用 NEXT 搭建后台服务接口 https://docs.nestjs.com/ # 准备工作 安装 node 全局安装 nest npm i -给nestjs/cli nest --version # 创建项目 创建项目next new 启动项目npm run start 或 npm run start:dev 访问接口 localhost:3000 获取命令解释 next g -h # 快速创建…

Pixracer接线图 及电调调参 BLheliSuite

Pixracer接线指南 pixracer官方链接 正反面引脚定义 接口含义 BLheliSuite调参软件 官方下载&#xff1a; https://www.mediafire.com/folder/dx6kfaasyo24l/BLHeliSuite 我使用了如下软件https://www.mediafire.com/file/9uccf1zy3wqb1w5/BLHeliSuite32_32.9.0.3.zip/fil…

Bio-Net:编解码器结构的循环双向连接网络

目录 摘要 方法 循环双向跳跃连接 前向跳跃连接 后向跳跃连接 递归的推断训练 BiO-Net网络结构 总结 摘要 对UNet以前的扩展主要集中对现有模块的改进或者提出新的模块来提高性能。因此这些变量通常会导致模型的复杂性不可忽视的增加。为了解决这种复杂性的问题。在本…

redis cluster 集群安装

redis cluster 集群安装 redis集群方案 哨兵集群 如图&#xff0c;实际上还是一个节点对外提供服务&#xff0c;所以虽然是三台机器&#xff0c;但是还是一台机器的并发量&#xff0c;而且master挂了之后&#xff0c;整个集群不能对外提供服务 cluster集群 多个主从集群节点…

五、伊森商城 前端基础-Vue 整合ElementUI快速开发 p28

目录 一、安装 1、安装ElementUI 2、在main.js文件中引入 2.1、引入ElementUI组件 2.2、让Vue使用ElementUI组件 二、使用 1、在hello.vue组件使用单选框 2、使用ElementUI快速搭建后台管理系统 2.1、修改App.vue 3、修改功能成动态显示 3.1、编写快速生成组件的模板 3…

java计算机毕业设计ssm学习互助平台网站8f554(附源码、数据库)

java计算机毕业设计ssm学习互助平台网站8f554&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff0…

C++11之引用

文章目录目的为啥要引入右值引用什么是右值引用右值引用作用移动构造函数移动语义 std::move移动语义注意事项完美转发博客目的 了解对应左值引用&#xff0c; 右值引用&#xff0c;移动语义&#xff0c; 完美转发含义。 右值引用&#xff08;及其支持的移动语义Move semanti…

1562_AURIX_TC275_电源监控

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 这一次的学习笔记内容比较少&#xff0c;因为有几页的文档内容跟之前看过的DataSheet内容雷同。因此&#xff0c;相应的学习笔记不再整理。 之前的学习笔记&#xff1a; (56条消息) 1451_…

Python学习基础笔记四十——os模块

os模块是与操作系统交互的一个接口。 os的方法Linux命令备注os.getcwd()pwd获取当前工作目录路径os.chdir()cd切换当前工作目录os.makedirs(dirname1/dirname2)mkdir -p dirname1/dirname2生成多级目录os.removedirs(dirname1)rmdir删除多级目录os.mkdir(dirname)mkdir dirnam…

JAVA入门零基础小白教程day04-数组

day04_java基础 课程目标 1. 【掌握】 IDEA的基本使用 2. 【理解】 什么是数组 3. 【掌握】 数组的定义及初始化 4. 【理解】 数组的内存图 6. 【理解】 数组常见的问题 7. 【掌握】 数组的案例 8. 【理解】 二维数组开发工具 一维数组 什么是数组 数组就是存储数据长度固定…

【Linux】源码安装Apache、Mysql、PHP以及LAMP部署验证

文章目录源码安装相关理论源代码安装特点源码包安装步骤一、源码安装Apache1、编译安装依赖包 apr2、编译安装依赖包 apr-util3、编译安装依赖包 pcre4、编译安装 Apache5、重启 apache 服务6、修改网页显示内容7、访问测试二、源码安装Mysql1、把系统自带的 boost 库卸载&…

【mmdetection系列】mmdetection之loss讲解

目录 1.configs 2.具体实现 3.调用 3.1 注册 3.2 调用 配置部分在configs/_base_/models目录下&#xff0c;具体实现在mmdet/models/loss目录下。 1.configs 有的时候写在head中作为参数&#xff0c;有的时候head内部进行默认调用。 我们以为例&#xff08;这里没有直接…

linux timer浅析

linux timer 1、数据结构 1.1 timer_list struct timer_list {struct hlist_node entry;unsigned long expires;void (*function)(struct timer_list *);u32 flags;#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map; #endif };entry:定时器保存到哈希表中的节点&am…

QT+Python停车场车牌识别计费管理系统

程序示例精选 Python停车场车牌识别计费管理系统 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff01; 前言 QTPython是非常经典的窗体编程组合&#xff0c;功能完善&#xff0c;可视化界面美观易维护&#xff0c;这篇博客针对停车场车牌识别计费方面编写代…

JavaScript前端实用的工具函数封装

这篇文章主要为大家介绍了JavaScript前端实用的一些工具函数的封装&#xff0c;有需要的朋友可以借鉴参考下&#xff0c;希望能够有所帮助! 1.webpack里面配置自动注册组件 第一个参数是匹配路径,第二个是深度匹配,第三个是匹配规则 const requireComponent require.contex…

20-Django REST framework-Serializer序列化器

Serializer序列化器前言序列化器作用定义Serializer定义方法字段与选项创建Serializer对象序列化使用基本使用增加额外字段关联对象序列化反序列使用模型类序列化器ModelSerializer指定字段前言 本篇来学习Serializer序列化器知识 序列化器作用 进行数据的校验对数据对象进行…

[附源码]计算机毕业设计基于VUE的网上订餐系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…