Java中的泛型历来烧脑,从Object到上界下界修罗界,各种神奇的编译错误让泛型编程痛苦不堪。在这篇里,我总结一下泛型声明方式。
最原始的泛型方式——抄袭Object!
Java中,一切对象基于Object。所以,当没有泛型可用的远古时代,一切泛型,都通过Object来实现。首先让我们设计一个与相同类型的对象对比的接口。
// 使用Object作为基类完成泛型
interface Cmp_0 {
int compareTo(Object o);
}
这样的方法确实极为简单。可是问题是显然的,参数o的类型无法保障,随时传进来一个鬼东西,触发异常。
简单泛型——TTT
为了解决上述的问题,不妨把C++的泛型语法借鉴过来,一个T简明易懂。
// 最简单的泛型方式
interface Cmp_1<T> {
int compareTo(T o);
}
这样,我们就能解决比较对象不一致的问题了。可是这就解决了所有问题了吗?
类型限制+一致性——extends
参见下面的场景:我想声明一个只能储存Classed及其子类型的类Party,并且所有Classed类型的对象都是同一种子类型的。说人话就是:同一阶级的人组成党派。
一个自然地想法是使用Classed来声明成员变量。
class Party_0 {
// 领袖
private Classed leader;
// getters, settters
}
然而这会滋生一个问题,比如一个无产阶级的党派可以设置资本家作为领袖,这种Naive的事情不能发生。而现在的泛型方式,虽然能够保证类型的一致,但是不能保证这个类型是Classed子类。
class Party_1<T> {
// 领袖
private T leader;
// getters, setters
}
直接通过父类声明,可以解决继承的限制;通过泛型,可以解决类型的一致性问题。它们结合起来呢?
Java用extends
关键字来的表明两个类的继承关系,这里Java讲这种关系延伸到了泛型之中。因为泛型类基于extends
后面的类,所以称这种声明为上界。
针对刚才的问题,我们写如下的代码 :
// T必须是Classed或者其子类
class Party_2<T extends Classed> {
private T leader;
}
通过描述T和Classed关系,保证了任何T都是Classed的子类,并且leader一定是类型T的,这样就解决了对类型限制和一致性的问题。
下界为子而生——super
然而只有上界还是不够的。让我们来看这样一个问题。
任何游戏都基于CF,而基于CF的游戏,他们的粉(nao)丝(can)喜欢撕逼。也就是说,它们是Sibiable的。
// 可撕逼的
interface Sibiable<T> {
// 对啦,撕逼能有什么结果呢?笑了
void sibi(T sb);
}
// 游戏起源CF
class Cf implements Sibiable<Cf> {
}
// DNF以F结尾,抄袭CF
class DNF extends Cf {
}
当然,能撕逼的不仅仅有游戏,还有动漫。任何动漫都是来源于《喜羊羊(HappyShit)》的,所有的动漫,也都可以撕逼。
// 动漫屎祖喜羊羊
class HappyShit implements Sibiable<HappyShit> {
}
// 黄段子当然也抄袭了喜羊羊
class YellowSegments extends HappyShit {
}
如果我们需要编写一个列表,来记录下所有可以撕逼的东西怎么办呢?利用刚才上界,我们可以写出这样的代码。
class SibiList_0<T extends HappyShit> {
}
class SibiList_1<T extends Cf> {
}
然而这并不满足我们的需求。我们脑洞大开一下,使用一个嵌套的约束呢?
class SibiList_2<T extends Sibiable<T>> {
}
我们很容易发现,这样做除了Cf、HappyShit两个基类外,他们任何的子类都不能添加进去!因为他们子类实现的是Sibiable<Cf>接口或者Sibiable<HappyShit>,而类的要求是必须要实现自身作为参数的Sibiable接口。
如果一个类Self实现了Sibiable<Self>,那么它的子类都实现了这个接口。Self这个类,与它的子类是超类与子类的关系,也就是super
。那么将这种关系推广到泛型之中不就好了嘛?于是我们有了这样的代码:
class SibiList_3<T extends Sibiable<? super T>> {
}
这里的?就是对一个泛型的使用。从内向外理解一下就是:
首先要一个类它是T的超类,这个类作为Sibiable的参数类型。而T是这个Sibiable的子类。说人话就是:T的父类实现了Sibiable<自身>的接口。
总结
Java的泛型机制从最原始的Object到最终的上下界限制,不断优化迭代,使用更复杂的语法,使之更符合需求。