type
status
date
slug
summary
tags
category
password
icon
泛型类型的创建
泛型类的创建
泛型接口的创建
泛型作⽤⼩结
- 帮助检查代码中的类型,提前报错;
- ⾃动强制转型。
「创建⼀个泛型类型」到底是为了什么?
- 本质⽬标或原因:这个类型的不同实例的具体类型可能会有不同,针对的是实例
- 因此,静态字段和静态⽅法不能使⽤泛型类型的类型参数(也就是那个
T
)
继承
类型参数 <T>
到底是什么
- 不是⼀个类,也不是⼀个接⼝。只是⼀个标记符号,⼀个代号
- 代什么?代表这个类型内部某个通⽤的类型
多个类型参数
泛型类型的实例化
- 其实就是「确定这个 T 的实际值」的意思
泛型类型实例化
上界
<? extends Xxx>
用处
下界
<? super Xxx>
用处?
java 的范型存在类型擦出
数组有这个问题吗?
没有
为什么?
因为数组没有「类型擦除」
类型擦除是怎么回事?——后⾯有专⻔的⼀节课讲。
泛型⽅法和类型推断
怎么写
声明
调用:
利⽤类型推断删除尖括号后:
泛型⽅法的实例化
泛型⽅法也有实例化哟,因为泛型⽅法也可以把类型参数的类型进⾏确定。
具体呢?每⼀次泛型⽅法的调⽤就是⼀次对这个泛型⽅法的实例化。
例如上⾯的例⼦,就是把
E
的类型确定为了 Tv
。静态泛型⽅法
泛型的本质:我到底什么时候要使⽤泛型
泛型的意义
- 泛型的意义在于:泛型的创建者让泛型的使⽤者可以在使⽤时(实例化时) 细化类型信息,从⽽可以触及到「使⽤者所细化的⼦类」的 API。 或者,泛型是「有远⻅的创造者」创造的「⽅便使⽤者」的⼯具。
- 所以泛型参数可以是⼀个⽅法的返回值类型:
- 也可以是放在⼀个接⼝的参数⾥,等着实现类去写出不同的实现
延伸⽤途
- 不过,泛型由于语法⾃身特性,所以也有⼀个延伸⽤途:类型约束。
情景再归纳
Type Parameter 和 Type Argument
- Type Parameter:
- public class Shop<T> ⾥⾯的那个 <T> ;
- 表示「我要创建⼀个 Shop 类,它的内部会⽤到⼀个统⼀的类型,这 个类型姑且称他为 T 」。
- Type Argument:
- 其它地⽅尖括号⾥的全是 Type Argument,⽐如 Shop<Apple> appleShop; 的 Apple ;
- 表示「那个统⼀代号,在这⾥的类型我决定是这个」。
Type Parameter
和Type Argument
;泛型的创建和泛型的实例化
T
情景归纳
- 写在类名(接⼝名)右边的括号⾥,表示 Type Parameter 的声明,「我要创建⼀个代号」;
- 写在类或接⼝⾥的其他地⽅,表示「这个类型就是我那个代号的类型」;
- 很容易搞错的场景:当继承或实现⼀个泛型类型的时候,如果⽗类(或⽗接 ⼝)名的右边也写了 <T> :
- 我要对⽗类(接⼝)进⾏实例化,即确定它类型参数的实际值
- 实例化的具体类型是我的这个类型参数
- 使⽤⾃⼰的类型参数(Type Parameter)来作为
这个右边⽗类(接⼝)右边的 <T> 表示:
所以它从⽗类和⾃⼰这两个不同⻆度来看,同时具有「已确定的实例化」和「未确定的声明」这两个属性。
- 只在这个类或者接⼝⾥有⽤,出了类或接⼝就没⽤了(对于泛型⽅法是,只在这 个⽅法⾥有⽤,出了这个⽅法就没⽤了)。
- 所以不能这么写:
- 也不能这么写:
?
号情景归纳
- 只能写在泛型实例化的地⽅:
表示「这个类型是什么都⾏,只要不超出 ? extends 或者 ? super 的限
制」。
- 虽然⽤于实例化,但因为它表示「类型还有待进⼀步确定」,它不能⽤在类型参 数最终确定的时候:
- 「类型还有待进⼀步确定」的特殊场景:嵌套泛型实例化:
包在多层尖括号⾥⾯的实例化,因为全都属于「类型有待进⼀步确定」, 所以全都可以加问号。
extends
情景归纳
- ⽤在泛型类型或泛型⽅法声明的时候,对类型参数设置上界
- ⽤在泛型实例化时 ? 号可以出现的地⽅,⽤来设置上界,缩⼩ ? 号的限制范 围。
super
情景归纳
- 只有⼀个地⽅:⽤在泛型实例化时
?
号可以出现的地⽅,⽤来设置下界,缩⼩?
号的限制范围。
泛型中的「重复」和「嵌套」
<T>
的重复:
表示对⽗类(⽗接⼝)的扩展。
类名的重复:
同样表示对⽗类(⽗接⼝)的扩展(囧)
正常的实例化嵌套:
这个不⽤多解释。
类型参数的上界是⼀个泛型类型:
有⼈在这⾥会有点晕,但其实只要分开看: HenCoderList<T> 是⼀个东⻄; Shop<Apple> 是另⼀个东⻄。要完全分开分析。
同理,就算再复杂⼀点:
也是⼀样的分开分析就好了。
极端案例:Enum
extends Enum<E>
表示⼀个上界,即「E
需要是Enum<E>
的⼦类」
- 这是个
Comparable<E>
的实现,所以compareTo(E o)
的参数就需要是 个Enum<E>
的⼦类 - 进⼀步:这个类也是⼀个
Enum<E>
,所以表示「你必须和跟⾃⼰⼀样的 类作⽐较」
Enum
这个类还有⼀个getDecalaringClass()
⽅法:
所以这个⽅法的返回值也需要是⼀个
Enum<E>
的⼦类。获取参数和返回值中的泛型
泛型中的类型擦除和「不可以」以及突破 「不可以」
- 运⾏时,所有的
T
以及尖括号⾥的东⻄都会被擦除
List
和List<String>
以及List<Integer>
都是⼀个类型;
- 但是所有代码中声明的变量或参数或类或接⼝,在运⾏时可以通过反射获取到泛 型信息;
- 但但是,运⾏时创建的对象,在运⾏时通过反射也获取不到泛型信息(因为 class ⽂件⾥⾯没有);
- 但但但是,有个绕弯的⽅法就是创建⼀个⼦类(哪怕⽤匿名类也⾏),⽤这个⼦ 类来⽣成对象,这样由于⼦类在 class ⽂件⾥就有,所以可以通过反射拿到运⾏
- 时创建的对象的泛型信息。
⽐如 Gson 的
TypeToken
就是这么⼲的。
Kotlin 的泛型
- 场景跟 Java ⼀样,不过⽤法有⼀点不⼀样;
- Java 的
<? extends>
在 Kotlin ⾥写作<out>
;Java 的<? super>
在Kotlin ⾥写作<in>
;
- 另外,Kotlin 还增加了
out T
in T
的修饰,来在类或接⼝的声明处就限制使⽤,这样你在使⽤时就不必再每次都写;
- Kotlin 的
*
号相当于 Java 的?
号,基本⼀样,只是有些细节不⼀样
- 作者:shuouyang
- 链接:https://notion-tree.vercel.app/article/bcc02828-3ff3-4ccf-af05-a89d936b6826
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。