惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
博客园 - Franky
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
GbyAI
GbyAI
Y
Y Combinator Blog
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
博客园 - 叶小钗
罗磊的独立博客
The GitHub Blog
The GitHub Blog
H
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Engineering at Meta
Engineering at Meta
Martin Fowler
Martin Fowler
Spread Privacy
Spread Privacy
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
T
Threatpost
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
T
Tor Project blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
L
Lohrmann on Cybersecurity
量子位
V
V2EX
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Proofpoint News Feed
I
Intezer
Schneier on Security
Schneier on Security
雷峰网
雷峰网
B
Blog RSS Feed
Jina AI
Jina AI
爱范儿
爱范儿
Attack and Defense Labs
Attack and Defense Labs
Google DeepMind News
Google DeepMind News
M
MIT News - Artificial intelligence
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
美团技术团队
博客园 - 【当耐特】

博客园 - 一个人的合唱

手写简易docker synchronized 反码与补码 解决128位秘钥长度限制的方法 CAP学习 ThreadLocal的学习 读书笔记脑图列表 a=a++和a=++a centos7下安装python3 深入理解jvm虚拟机笔记(二) AtomicInteger的使用 volatile学习 echarts重新绘制的时候数据未更新 深入理解jvm虚拟机笔记(一) elasticsearch学习笔记 使用fiddler模拟post请求 二叉堆 二叉平衡树 二叉查找树
泛型学习
一个人的合唱 · 2026-03-21 · via 博客园 - 一个人的合唱

泛型

技术背景

Java是强类型语言 方法里的参数和返回值类型需要预先声明 但很多时候一段代码可以作用于多种数据类型 那么需要复制粘贴很多次这段代码 但这些代码只有参数类型或返回值类型不同而已 这时候就可以使用泛型来解决。

实现

泛型的字面意思是广泛的类型 它其实是类型参数化。先用类型参数占位 然后到用时才将实际类型作为参数传进来 之后泛型占位的地方替换成该实际类型来运行 。其实现是通过类型擦除来实现的 编译器将泛型替换为Object 然后对象进来的时候检查对象类型跟实际类型是否相同 如果不同那么报错 如果相同 那么用Object接收 而对象出去的时候从Object强制转换成对应的实际类型。所以泛型只是比完全用Object 编译器多做了类型检查和强制类型转换的工作而已 这样更安全也更易读。

上界

但是完全当做Object处理的话 还是很不方便。而如果限制了参数类型的上界的话 比如声明 T extends Number 那么传进来的类型一定是Number的子类 所以可以将其当做Number来处理 到类型擦除时不是用Object来代替泛型 而是Number。
泛型类T<Number> 不是泛型类T<Number子类Integer>的父类 否则前者的对象a可以指向后者的对象b 之后往a里添加Number的其他子类比如Double 但a其实指向b对象 那么相当于往Integer里添加Double 到时取出时编译器添加的强制类型转换语句将Double强制转成Integer会报错。但如果想把泛型类里的元素添加到泛型类T<Number> 可以声明参数类型为T extends Number 那么就会把Number的子类放入T<Number> 然后之后都当做Number来使用。

通配符

泛型方法与通配符

<T extends E>是定义类型参数 它声明了一个类型参数T 可以放在泛型类定义中类名后面、泛型方法返回值前面。而 <? extends E> 是实例化类型参数 它用于实例化泛型变量中的类型参数 只是这个具体类型是未知的 只知道它是E或者E的某个子类型。但它们经常可以达成相同目标。
因为通配符只声明了是E的某个子类型 无法确定具体的子类型 所以它只能读不能写入 否则写入后取出时涉及到强制类型转换 会报错。必须时借助带类型参数的泛型方法来完成。
有依赖关系的只能用类型参数表示 如拷贝对象 那么to必须得是from的父类 方法定义为 <from extends to,to> copy(from ,to) 但可以用通配符简化 如<to> copy(? extends to ,to) 。
泛型方法和通配符的关系总结如下:

  • 通配符形式都可以用类型参数形式表示 通配符能做的 类型参数都能做
  • 通配符形式可以减少类型参数 形式更简单 可读性更好 所以能用通配符就用通配符
  • 如果类型参数之间有依赖关系 或者返回值依赖类型参数 或者需要写操作 则只能用类型参数。

超类型通配符

<? extends E> 跟 <? spuer E> 意思恰好相反 <? extends E>限制进来的对象的类型是E的子类 因此可以将进来的对象用E接收 而<? spuer E>限制进来的对象的类型是E的父类 所以可以将E交给传进来的对象。
实现Comparable接口是一个很常见的业务 但假如Base类实现了Comparable接口 而Child继承自Base类 那么对于要求参数为<T extends Comparable<T>>的方法它不满足条件 因为Child继承自Base之后是Comparable<Base> 而不是Comparable<Child> 所以应该把参数改为 <T extends Comparable<? super T>> 这样只要求其父类实现了Comparable接口 然后Child才会满足条件。

通配符总结

  • 对于<?>和<? extends E>以及 <? super E>都是为了方法和接口更灵活 可以接受更广泛的类型
  • 是为了 灵活写入或比较 使得对象E可以写入父类型的容器 使得父类型的比较方法可以应用于子类对象 它不能被类型参数形式替代
  • 和是为了 灵活读取 使得方法可以读取E和E的任意子类型的容器对象 他们可以用类型参数的形式替代 但通配符形式更为简洁

疑难杂点

关于类型擦除

可以把<>当做注释符号看待 因为编译之后泛型类声明的<T>会被类型擦除 所以每个泛型类只有一份class类型对象。
对于前面的Child类 如果它想自定义自己的比较方法 那么不能实现Comparable<Child>接口 只能覆盖Base的比较方法。而且方法A(Comparable<Base>) 和方法A(Comparable<Child>)是相同方法 不是参数不同的重构方法。

定义泛型类、方法和接口

想通过类型参数创建对象只能通过反射。
对于泛型类声明的类型参数 也就是在类名后定义的 可以在实例变量和方法中使用 不能在静态变量和静态方法中使用 因为如果能的话 那么对于每种实例化类型都要有一份对应的静态变量和静态方法 而由于类型擦除 泛型类只有一份class类型对象 静态变量和静态方法是class类型对象的属性 且与类型参数无关 所以不能使用泛型类的类型参数。而如果静态方法是泛型方法 可以声明自己的类型参数 但这与泛型类的类型参数无关。

泛型与数组

Java不支持创建泛型数组 如果要存放泛型对象 要么使用泛型类型的原始类型的数组 或者使用泛型容器 泛型容器内部使用Object数组 如果要转换泛型容器为对应类型的数组 需要使用反射。