聊聊 Java 泛型

2023-11-02

概述

什么是泛型?为什么需要泛型?如何使用?是什么原理?如何改进? 这基本上就是我们学习一项技术的正确套路,本文将按照以上顺序展开阐述,介绍我所了解的泛型。

什么是泛型

泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

为什么需要泛型

Java中引入泛型最主要的目的是将类型检查工作提前到编译时期,将**类型强转(cast)**工作交给编译器,从而让你在编译时期就获得类型转换异常以及去掉源码中的类型强转代码。例如

泛型作用的对象

泛型有三种使用方式,分别为:泛型类、泛型接口和泛型方法。

泛型类

在类的申明时指定参数,即构成了泛型类,例如下面代码中就指定T为类型参数,那么在这个类里面就可以使用这个类型了。例如申明T类型的变量name,申明T类型的形参param等操作。

 

java

复制代码

public class Generic<T> { public T name; public Generic(T param){ name=param; } public T m(){ return name; } }

那么在使用类时就可以传入相应的类型,构建不同类型的实例,如下面代码分别传入了String,Integer,Boolean3个类型:

 

java

复制代码

private static void genericClass() { Generic<String> str=new Generic<>("总有刁民想害朕"); Generic<Integer> integer=new Generic<>(110); Generic<Boolean> b=new Generic<>(true); System.out.println("传入类型:"+str.name+" "+integer.name+" "+b.name); }

输出结果为:传入类型:总有刁民想害朕 110 true

如果没有泛型,我们想要达到上面的效果需要定义三个类,或者一个包含三个构造函数,三个取值方法的类。

泛型接口

泛型接口与泛型类的定义基本一致

 

java

复制代码

public interface Generator<T> { public T produce(); }

泛型方法

这个相对来说就比较复杂,当我首次接触时也是一脸懵逼,抓住特点后也就没有那么难了。

 

java

复制代码

public class Generic<T> { public T name; public Generic(){} public Generic(T param){ name=param; } public T m(){ return name; } public <E> void m1(E e){ } public <T> T m2(T e){ } }

重点看public <E> void m1(E e){ }这就是一个泛型方法,判断一个方法是否是泛型方法关键看方法返回值前面有没有使用<>标记的类型,有就是,没有就不是。这个<>里面的类型参数就相当于为这个方法声明了一个类型,这个类型可以在此方法的作用块内自由使用。 上面代码中,m()方法不是泛型方法,m1()m2()都是。值得注意的是m2()方法中声明的类型T与类申明里面的那个参数T不是一个,也可以说方法中的T隐藏了类型中的T。下面代码中类里面的T传入的是String类型,而方法中的T传入的是Integer类型。

 

java

复制代码

Generic<String> str=new Generic<>("总有刁民想害朕"); str.m2(123);

通配符?

?代表任意类型,例如有如下函数:

 

java

复制代码

public void m3(List<?>list){ for (Object o : list) { System.out.println(o); } }

其参数类型是,那么我们调用的时候就可以传入任意类型的List,如下

 

java

复制代码

str.m3(Arrays.asList(1,2,3)); str.m3(Arrays.asList("总有刁民","想害","朕"));

但是说实话,单独一个意义不大,因为大家可以看到,从集合中获取到的对象的类型是Object 类型的,也就只有那几个默认方法可调用,几乎没什么用。如果你想要使用传入的类型那就需要强制类型转换,这是我们接受不了的,不然使用泛型干毛。其真正强大之处是可以通过设置其上下限达到类型的灵活使用,且看下面分解重点内容

通配符上界

通配符上界使用<? extends T>的格式,意思是需要一个T类型或者T类型的子类,一般T类型都是一个具体的类型,例如下面的代码。

 

java

复制代码

public void printIntValue(List<? extends Number> list) { for (Number number : list) { System.out.print(number.intValue()+" "); } }

这个意义就非凡了,无论传入的是何种类型的集合,我们都可以使用其父类的方法统一处理。

通配符下界

通配符下界使用<? super T>的格式,意思是需要一个T类型或者T类型的父类,一般T类型都是一个具体的类型,例如下面的代码。

 

java

复制代码

public void fillNumberList(List<? super Number> list) { list.add(new Integer(0)); list.add(new Float(1.0)); }

至于什么时候使用通配符上界,什么时候使用下界,在**《Effective Java》中有很好的指导意见:遵循PECS原则,即producer-extends,consumer-super.** 换句话说,如果参数化类型表示一个生产者,就使用 <? extends T>;如果参数化类型表示一个消费者,就使用<? super T>

Java泛型原理解析

为什么人们会说Java的泛型是伪泛型呢,就是因为Java在编译时擦除了所有的泛型信息,所以Java根本不会产生新的类型到字节码或者机器码中,所有的泛型类型最终都将是一种原始类型,那样在Java运行时根本就获取不到泛型信息。

擦除

Java编译器编译泛型的步骤:

  1. 检查泛型的类型 ,获得目标类型
  2. 擦除类型变量,并替换为限定类型(T为无限定的类型变量,用Object替换)
  3. 调用相关函数,并将结果强制转换为目标类型。

如何擦除: 当擦除泛型类型后,留下的就只有原始类型了,例如上面的代码,原始类型就是ArrayList。擦除类型变量,并替换为限定类型(T为无限定的类型变量,用Object替换),如下所示

擦除之前:

 

java

复制代码

//泛型类型 class Pair<T> { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } }

擦除之后:

 

java

复制代码

//原始类型 class Pair { private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }

因为在Pair<T>中,T是一个无限定的类型变量,所以用Object替换。如果是Pair<T extends Number>,擦除后,类型变量用Number类型替换。

Gson是如何把泛型转换成目标类型的

TypeToken

 

java

复制代码

/** * Represents a generic type {@code T}. Java doesn't yet provide a way to * represent generic types, so this class does. Forces clients to create a * subclass of this class which enables retrieval the type information even at * runtime. * * <p>For example, to create a type literal for {@code List<String>}, you can * create an empty anonymous inner class: * * <p> * {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};} * * <p>This syntax cannot be used to create type literals that have wildcard * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}. * * @author Bob Lee * @author Sven Mawson * @author Jesse Wilson */ public class TypeToken<T> { final Class<? super T> rawType; final Type type; final int hashCode; /** * Constructs a new type literal. Derives represented class from type * parameter. * * <p>Clients create an empty anonymous subclass. Doing so embeds the type * parameter in the anonymous class's type hierarchy so we can reconstitute it * at runtime despite erasure. */ @SuppressWarnings("unchecked") protected TypeToken() { this.type = getSuperclassTypeParameter(getClass()); this.rawType = (Class<? super T>) $Gson$Types.getRawType(type); this.hashCode = type.hashCode(); } ...... }

 

java

复制代码

public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { if (json == null) { return null; } return (T) fromJson(new JsonTreeReader(json), typeOfT); }

 

java

复制代码

//只要调用fromJson传入一个TypeToken的子类就能获取到泛型的类型 new Gson().fromJson(json,new TypeToken<ArrayList<String>>(){}.getType());

原理

子类如果实例化这个泛型,则编译时会保留实例化类型,可以通过反射获取,如:

 

java

复制代码

Generic<String> generic = new Generic<String>(){ }; System.out.println(generic.getClass().getGenericSuperclass());

以上类型会输出:com.hencoder.layoutlayout.Generic<java.lang.String>

作者:PuddingSama
链接:https://juejin.cn/post/7233720284708044859

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

聊聊 Java 泛型 的相关文章

随机推荐

  • 利用python爬取招聘网站上的相关岗位信息

    这段代码是一个爬取Boss直聘网站上数据岗位信息的爬虫程序 它使用了异步编程库asyncio和网页自动化测试库pyppeteer来实现异步的网页爬取和数据提取 import asyncio 异步编程库 import random 随机数生成
  • 不用看网课就能学到python的文章(第三天)

    紧接着上一篇不用看网课就能学到python的文章 第二天 Why does it work的博客 CSDN博客 如果说到语句 那我们应该了解一些一些python python最具特色的就是使用缩进来表示代码块 不需要使用大括号 行与缩进 i
  • google Guava之EventBus

    文章目录 EventBus基本用法 1 创建Listener 2 创建EventBus并发送消息 Listener之间的继承关系 Subscriber 不同类型参数的Subscribe event 继承关系的event DeadEvent
  • 树莓派3B安装python3 opencv环境

    树莓派3b 若干年前买的 软件具体配置不记得 python缺省版本为3 53 直接用pip命令安装不成功 显示一堆的红字 update upgrade一下 耗时挺长 再安装 成功了 如下图 sudo apt get update sodu
  • C++11 并发指南系列

    本系列文章主要介绍 C 11 并发编程 计划分为 9 章介绍 C 11 的并发和多线程编程 分别如下 C 11 并发指南一 C 11 多线程初探 本章计划 1 2 篇 已完成 1 篇 C 11 并发指南二 std thread 详解 本章计
  • 微信小程序:cavans的使用(一)

    最近用到这个canvas画布这个组件 其实还是挺有意思的 优点是可以给用户绘制宣传海报什么的 这个现在好多小程序都有这个 比如给你一个专属海报有你的头像还有你的二维码 你可以保存在你的相册里 你可以取拿它去拉人 微信小程序canvas组件官
  • 因为好兄弟一句话竞。。。

    职教云查同学考试成绩 本来在打游戏 好兄弟微信问了我一句职教云未公开的考试能不能 打包好的软件下载地址在文章后面 然后在想 考试未公开 只有那老师能看班里同学成绩 先试了抓一下包 发现在学生端啥也没有只能获得考试的ID 好家伙 突然灵光一闪
  • 继承与多态(Java实现)

    继承和多态 一 类的继承 1 继承的实现 语法格式 class 子类名 extends 父类名 类体 注 extends是关键字 Java中只支持单继承 所以子类只有一个父类 但可以多层继承 子类通过继承可以获得父类的public prot
  • PCL 计算点云的高斯曲率和平均曲率

    目录 一 算法原理 二 代码实现 三 结果展示 四 相关链接 一 算法原理 已知某点的主曲率为 k 1 k 2 k 1 k 2 k
  • 基于Opencv实现的多彩隔空画图

    1 问题概述 人工智能带火了计算机视觉的人才需求 作为计算机视觉应用开发框架OpenCV也越来越受到欢迎 市场需求大增 因此 在学习Python的基础上 进行Opencv技术的学习是十分重要且有必要的 该项技术不仅有着成熟的学习框架 更有广
  • 淘宝应对"双11"的技术架构分析

    http www uml org cn zjjs 201311272 asp http www uml org cn 火龙果软件
  • Ubuntu 14.04安装TeamViewer

    1 到官网下载teamviewer的deb包 2 拷贝到 下 打开文件 双击 deb包 3 在Ubuntu软件中心中点击安装 如果缺少依赖包 sudo apt get install f修复依赖关系
  • UAV021(五):STM32F429实现TIM6计时、TIM3输出4路PWM波、TIM5输入捕获

    目录 序 一 STM32F4定时器介绍 二 定时器配置 2 1 TIM6实现计数 2 2 TIM3输出4路PWM波 2 3 TIM5输入捕获 序 现在需要实现计时 输出PWM和输入捕获 其中计时实现0 1ms加1 用于陀螺仪积分计时 输出4
  • 离线win7上用anaconda离线创建虚拟环境

    本文所需文件的下载路径为 百度云链接 https pan baidu com s 14S xkERRhQVNfV dauVYzw 提取码 5hzn 第一步 安装anaconda win7系统不支持python3 9 因此不能在win7系统上
  • 凭借这5步,我30分钟学会了Python爬虫

    在不同公司的许多人可能出于各种原因需要从Internet收集外部数据 分析竞争 汇总新闻摘要 跟踪特定市场的趋势 或者收集每日股票价格以建立预测模型 无论你是数据科学家还是业务分析师 都可能时不时遇到这种情况 并问自己一个永恒的问题 我如何
  • win系统电脑如何打开sketch?

    2 个方法快速使用 Windows 系统打开 Sketch 文件 使用 Adobe XD 打开 Sketch 文件或者使用浏览器中就能做设计的即时设计直接打开 Sketch 文件 众所周知 Sketch 只能在 Mac 电脑上使用 因此只有
  • SQuirrel SQL Client数据库连接工具的配置与使用

    SQuirrel SQL Client介绍 SQuirrel SQL Client是一个用Java写的数据库客户端 用JDBC统一数据库访问接口以后 可以通过一个统一的用户界面来操作MySQL PostgreSQL MSSQL Oracle
  • Java Html嵌入applet 来读取客户端串口

    写在前面 之前没搞过html嵌入applet来读取本地客户端串口 就直接使用RXTXcom jar 来直接读取本机串口 这个是没问题的如下 RXTX 有三个文件 有针对操作系统64 还有32的 1 RXTXcomm jar 导入项目中 2
  • 【LaTeX】矢量图转为pdf格式(为了将高清矢量图插入LaTeX)

    在论文编写的时候 需要插入高清的矢量图 但是不同的图片生成软件 图片处理软件 论文编写软件所支持的矢量图格式都是不一致的 如 matplotlib可以保存的矢量图格式为 svg eps 等 visio可以保存的格式为 svg emf 等 但
  • 聊聊 Java 泛型

    概述 什么是泛型 为什么需要泛型 如何使用 是什么原理 如何改进 这基本上就是我们学习一项技术的正确套路 本文将按照以上顺序展开阐述 介绍我所了解的泛型 什么是泛型 泛型的本质是参数化类型 即给类型指定一个参数 然后在使用时再指定此参数具体