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

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 一个人的合唱

手写简易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数组 如果要转换泛型容器为对应类型的数组 需要使用反射。