Java泛型中的通配符<?>,<? extends T>,<? super T>

之前一直对Java泛型中的通配符不是很清楚,前几天专门研究了一下。
Java中的泛型通配符分为以下三种:

  • <? extends T> 子类型限定通配符
  • <? super T> 超类型限定通配符
  • <?> 无限定通配符

通配符的使用场景

通配符只有在修饰一个变量参数的时候会用到,在定义泛型类或泛型方法的时候是不能使用通配符的。

为了更好的说明泛型通配符的使用,我们使用代码示例来加以说明。首先我们创建一个类 A,是一个泛型类,里面保存一个变量 value

public class A<T> {
    private T value;
    // 省略 get 和 set 方法
    // ......
}

我们再创建两个类 Father 和 Son,Son 是 Father 的子类

// Father.java
public class Father {
}

// Son.java
public class Son extends Father{
}

思考下面的情况

public static void main(String[] args){
    A<Father> a1 = new A<Father>();
    A<Son> a2 = new A<Son>();
    test(a1);
    test(a2);  // 编译错误
}

public void test(A<Father> a){...}

Son 是 Father 的子类,但 test(A<Father> a) 方法却不能接收参数 a2,也就是说 A<Son> 不是 A<Father> 的子类。这个时候就可以使用通配符来解决,我们修改 test 方法

public static void main(String[] args){
    A<Father> a1 = new A<Father>();
    A<Son> a2 = new A<Son>();
    test(a1);
    test(a2);
}
public void test(A<? extends Father> a){...}

这样可以可以正常调用,说明类型 A<Son>A<? extends Father> 的子类型

我们清楚了通配符的使用场景,下面再分别看下几种通配符

<? extends T> 子类型限定通配符

我们上面的例子使用了子类型限定通配符,使用通配符很方便,但也带来了一些问题,我们接着看代码

public void test(A<? extends Father> a){
    a.setValue(new Father()); //编译错误
    a.setValue(new Son()); //编译错误
    Father father = a.getValue();
}

你会发现我们根本不能调用 setValue 方法,假想一下 A<? extends Father> 类,里面的方法似乎是这样的

// 这不是真正的Java方法,只是为了说明
? extends Father getValue();
void setValue(? extends Father);

当我们调用 setValue 方法的时候,编译器只知道需要某个 Father 及其子类型,但不知道具体是什么类型,所以它拒绝传递任何的特定类型。

使用 getValue 就不会有问题,因为返回值肯定是 Father 及其子类型,所以我们把返回值赋给一个 Father 的引用完全合法。

<? super T> 超类型限定通配符

超类型限定通配符的行为与上面说的子类型限定通配符相反,可以为方法提供参数,但不能使用返回值。我们看下面的例子:

public void test2(A<? super Father> a){
    a.setValue(new Father());
    Father father = a.getValue(); // 编译错误
    Object object = a.getValue(); 
}

假想一下 A<? super Father> 类,里面的方法似乎是这样的

// 这不是真正的Java方法,只是为了说明
void setValue(? super Father)
? super Father getValue() 

setValue 方法不知道参数的具体类型,但是可以确定的是参数肯定是 Father 及其父类型,所以我们传递 Father 及其子类型是合法的。

调用 getValue 方法不能保证返回类型的对象,所以只能赋给一个 Object。

<?> 无限定通配符

还可以使用无限定通配符 <?> ,这种方式,不能为方法提供参数,调用方法返回值也只能赋给 Object。

public void test3(A<?> a){
    Object object = a.getValue();
    a.setValue(new Object()); //编译错误
}

getValue 的返回值只能赋给一个 Object,setValue 方法不能被调用(注意:可以调用 setValue(null))。

所以感觉无限定通配符是集合了上面说的两种通配符的缺点,那我们为什么还要使用它呢?其实在某些简单的场景还是有用的,例如下面这种情况

// 判断A类中的值是否为空,并不关心A类中值具体是什么类型
public Boolean isNull(A<?> a){
    return a.getValue == null;
}

总结

看完了三种通配符的使用,我们来做个总结:

  • <? extends T> 子类型限定通配符
    无法向其中设置值,但是可以进行正常的取出
  • <? super T> 父类型限定通配符
    可以设置 T 类型及其子类型的对象,但是取出的时候只能赋值给 Object
  • <?> 无限定通配符
    无法向其中设置值,取值的时候也只能赋值给 Object

从上面的总结可以看出,<? extends T> 通配符偏向于内容的获取,而 <? super T> 通配符更偏向于内容的存入。

PECS 原则(Producer Extends Consumer Super)很好的解释了这两种通配符的使用场景:

  • Producer Extends 说的是当你的情景是生产者类型,需要获取资源以供生产时,建议使用 extends 通配符,因为使用了 extends 通配符的类型更适合获取资源。
  • Consumer Super 说的是当你的场景是消费者类型,需要存入资源以供消费时,建议使用 super 通配符,因为使用 super 通配符的类型更适合存入资源。

当然,如果你既想设置值又想取出值,那么就不适合使用通配符了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350