先说下本篇随笔主要涉及到的东西(参考Java编程思想一书):  

    1、说明 List<Fruit> 与 List<Apple> 之间为什么是非继承关系。

    2、由 1 引出的问题说明 List<? extends Fruit> 存在的必要性。

    3、说明 List<? super Fruit> 与 List<? extends Fruit> 的区别及 List<? super Fruit> 存在的必要性。

    4、说明 <? extends Fruit> 与 <T extends Fruit> 区别。

    5、说明 原生List 与 List<?> 区别。

    6、解释自限定泛型 class SelfBound<T extends SelfBound<T>>{ }。

    

    下面将会用到的三个有继承关系的类:

    

class Fruit {
}
class Apple extends Fruit {
}
class Orange extends Fruit{
}

  一、 List<Fruit> 与 List<Apple> 之间为什么是非继承关系。

    我认为以下两个原因可以解释这个问题:

    1、Java中泛型是后来引入的特性,为了兼容之前的代码,泛型是存在擦除机制的,List<Fruit> 与 List<Apple> 在擦除后的class中均为List,并不存在继承关系。

    2、从逻辑上解释不能有继承关系的原因:

      

         public void test(List<Fruit> list) {
   list.add(new Orange());
   }

      在上面的代码中,test方法接收一个List<Fruit> 类型的list,并在此list 中插入了一个Orange对象,这个方法是没有任何问题的。现在假设List<Fruit> 与 List<Apple> 间存在继承关系,那么此方法可以接收一个List<Apple> 类型的list 参数作为方法的参数,然而之后在方法中就会在一个声明是List<Apple> 的list 中插入一个 Orange 对象,这显然是不符合逻辑的。所以 List<Fruit> 与 List<Apple> 之间应该是非继承关系。

  二、 List<? extends Fruit> 存在的必要性。

      由一的介绍我们可以知道 test 方法只能只能接受 List<Fruit> 而不能接受 List<Apple> 类型的 list, 现在我想写一个方法,既能接受List<Fruit> 又能接收 List<Apple> 类型的 List,应该怎么做呢? 这时就要用到 List<? extends Fruit> 来实现这个功能了。

      List<? extends Fruit> 表示此 list持有的对象类型是 Fruit 或者从 Fruit 导出的类型(Apple 或者 Orange),相当于为 List 持有的对象类型规定了一个上界。

      我们只需将上面的test 方法改为 :

     

 public class TestGen {

     public void test(List<? extends Fruit> list) {
/*由于传入的参数是 List<? extends Fruit> 参数的list,这个list是不能进行add 操作的。
list.add(new Fruit());
list.add(new Orange());
list.add(new Apple());*/
} public static void main(String[] args) { List<Apple> list = new ArrayList<Apple>();
List<Orange> list1 = new ArrayList<Orange>();
List<Fruit> list2 = new ArrayList<Fruit>(); TestGen tg = new TestGen(); tg.test(list);
tg.test(list1);
tg.test(list2); }
}

      现在test 方法里的参数 变为了 List<? extends Fruit> list, 在 main 方法里可以看到此方法可以接受 List<Apple>,List<orange>,List<Fruit>多种类型的 list,实现了我们想要的功能。

      但是在上述代码的 test 方法当中我们也可以看到,作为参数传入的 list 是不能进行 add 操作的,无论 add 的是什么类型,这是为什么呢?

      原来由于传入的 List 类型是 List<? extends Fruit> list, 在JDK源码中可以看到 List的 add 方法的泛型参数就变为了 <? extends Fruit>,编译器并不能了解这里需要 add 的是哪个具体子类型,因此它

    不会接受任何类型的Fruit,编译器将直接拒绝对参数列表中涉及通配符的方法的调用。

      那么我们怎么才能做到向含有通配符的泛型限制的list 中做插入操作呢? 这时我们就要用到 <? super Fruit>参数。

  三、List<? super Fruit> 与 List<? extends Fruit> 的区别及 List<? super Fruit> 存在的必要性。

      由二可知, 要想向含有通配符的泛型限制的list 中做插入操作, 此泛型限制必须为  <? super someClass>。 前面已经说到,List<? extends Fruit> 为此List 可以持有的对象类型规定了一个上

    界,即持有的对象只能为 Fruit 或 Fruit 的子类型。 相应地, List<? super Fruit> 则为 List 可以持有的对象类型规定了一个下界,即持有的对象只能为 Fruit 或 Fruit 的超类。

      因此若将 test 方法改为下面这样,则 add 操作可以进行。

      

     public void test(List<? super Fruit> list) {
list.add(new Fruit());
list.add(new Orange());
list.add(new Apple());
}

      因为我们可以确定 传入 test 方法的参数至少也是一个 持有 Fruit 类型的 List,所以我们向此 list 中加入 Fruit 及Fruit 的子类型的对象是没有任何问题的。

  

  四、<? extends Fruit> 与 <T extends Fruit> 区别。

      两个东西应用的场景是不同的,<T extends Fruit>作用于方法或者类上,而 <? extends Fruit> 则不可以。下面举例说明。

      

 public class TestGen<T extends Fruit> {
private T value; public TestGen(T value) {
this.value = value;
} public T getValue() {
return value;
} public void setValue(T value) {
this.value = value;
} public <E extends Fruit> void test1(E e) {
System.out.println(e.getClass().getName());
} public static void main(String[] args) { TestGen<? extends Fruit> tg = new TestGen<Fruit>(new Fruit());
//由于 setValue 方法参数列表中涉及通配符(setValue方法中的 T 相当于 ? extends Fruit),setValue方法不能调用。
//tg.setValue(new Fruit()); tg.test1(new Fruit());
//tg.test1(new Object()); Object并不是Fruit的子类型,并不能作为参数传入test1方法。
}
}

      在代码中可以看到,类上的限定 <T extends Fruit> 及方法上的限定 <E extends Fruit> 使得类和方法能接受的类型均为 Fruit 及 Fruit 的子类型。

  五、原生List 与 List<?> 区别。

    无界通配符 ? 与 原生类型看似好像是等价的,但无界通配符可以有以下含义:“我是想用Java的泛型来编写这段代码,我在这里并不是要用原生类型,但是在当前这种情况下,泛型参数可以持有任何类型”。

    使用无界通配符的  List<?> 要比 原生 List 要安全些。 在原生 list 中,我们可以随意进行插入操作,可以向同一个 list 中既插入 object 对象,又插入 Fruit,Apple 对象,而 List<?> 是不允许进行 add 操作的。

  六、解释自限定泛型 class A<T extends A<T>>{ }。

    在 Java 泛型中,这算是一种经常出现的惯用法,这种用法意味着 基类 (这里的类A) 使用其导出类代替其参数,泛型基类变成了一种其所有导出类的公共公能的模板,但是这些功能对于其所有参数和返回值,将使用导出类型。

   下面举例说明。

    

 class A <T extends A<T>> {
void set(T arg) {
System.out.println("in A");
}
}
//泛型基类变成了一种其所有导出类的公共公能的模板
//导出类B和C均可调用基类的set方法,且只接受导出类对应的类型
class B extends A<B> { }
class C extends A<C> { }
public class TestGen {
void test2(B b1, B b2, C c1, A a1) {
b1.set(b2);
//使用自限定泛型时,在导出类中调用的set方法只接受对应的导出类型,不接受基类型和其他的导出类型
//b1.set(c1); 不接受其他的导出类型
//b1.set(a1); 不接受基类型,编译器不能识别将类型传递给set的尝试,因为没有任何方法有这样的
//签名,实际上,这个参数已经被覆盖
}
}

  本篇文章为作者原创,如果你觉得本篇文章对你有用的话,请随手点击推荐,让更多的人看到,感谢!

      

      

    

Java泛型<>内各种参数的异同的更多相关文章

  1. JAVA泛型与可变参数

    泛型的引入: 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题.也就存在这隐患,所以Java提供了泛型来解决这个安全问题. 格式: 泛型类:public class ...

  2. Effective Java 第三版——32.合理地结合泛型和可变参数

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  3. Java泛型中<?> 和 <? extends Object>的异同分析

    相信很多人和我一样,接触Java多年,却仍旧搞不清楚 Java 泛型中 <?>和 <? extends Object>的相似和不同.但是,这应该是一个比较高端大气上档次的Que ...

  4. 【Java 泛型】之 <? super T> 和<? extends T> 中 super ,extends如何理解?有何异同?

    Java 泛型 <? super T> 和<? extendsT>中 super ,extends怎么 理解?有何不同? 简介 前两篇文章介绍了泛型的基本用法.类型擦除以及泛型 ...

  5. 学习记录 java泛型资料

    java泛型资料: 1. 概述在引入范型之前,Java类型分为原始类型.复杂类型,其中复杂类型分为数组和类.引入范型后,一个复杂类型就可以在细分成更多的类型.例如原先的类型List,现在在细分成Lis ...

  6. Java泛型介绍!!!

    Java总结篇系列:Java泛型  转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...

  7. Java泛型学习笔记--Java泛型和C#泛型比较学习(一)

    总结Java的泛型前,先简单的介绍下C#的泛型,通过对比,比较学习Java泛型的目的和设计意图.C#泛型是C#语言2.0和通用语言运行时(CLR)同时支持的一个特性(这一点是导致C#泛型和Java泛型 ...

  8. Java 泛型在实际开发中的应用

    java泛型是对Java语言的类型系统的一种扩展,泛型的本质就是将所操作的数据类型参数化.下面我会由浅入深地介绍Java的泛型. 一:泛型出现的背景 在java代码里,你会经常发现类似下边的代码: p ...

  9. java 泛型基础问题汇总

    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引 ...

随机推荐

  1. Python内置:items()方法

    文章转载于:https://www.cnblogs.com/wushuaishuai/p/7738118.html(博主:IT技术随笔) #Python3中已取消iteritems()方法 描述 Py ...

  2. [转]RegExp 构造函数创建了一个正则表达式对象,用于将文本与一个模式匹配

    本文转自:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp RegExp  ...

  3. 第三章 Java内存模型(下)

    锁的内存语义 中所周知,锁可以让临界区互斥执行.这里将介绍锁的另一个同样重要但常常被忽视的功能:锁的内存语义 锁的释放-获取建立的happens-before关系 锁是Java并发编程中最重要的同步机 ...

  4. 10-16C#for...循环语句(2)

    for....循环语句 格式:for(初始条件:循环条件:状态改变) { 循环体: } 一.课前作业:打印等腰直角三角形 第一种方法:是运用一开始学习的从上往下执行控制台程序,用一个for循环语句执行 ...

  5. LAMP 2.7 Apache通过rewrite限制某个目录

    我们可以 allow 和 deny 去现在网站根目录下的某个子目录,当然这个 rewrite 也可以实现,配置如下: 创建一个目录和文件随便写些东西 mkdir /data/www/data/tmp ...

  6. LAMP 2.4 Apache访问控制

    通过查看日志发现有个IP 恶意攻击你的网址,可以控制这个IP的访问. 打开主配置文件复制模板. vim /usr/local/apache2/conf/httpd.conf 搜索 /Order 复制 ...

  7. cygwin选择安装包选项搭建NDK开发环境/配置cygwin的root权限

    9.Search是可以输入你要下载的包的名称,能够快速筛选出你要下载的包.那四个单选按钮是选择下边树的样式,默认就行,不用动.View默认是Category,建议改成full显示全部包再查,省的一些包 ...

  8. orancle数据库 插入数量 值大于 1000 解决方案

    存储过程:当基站ID大于1000的时候,把ID通过存储过程插入表,然后处理 不推荐这么弄,没办法,项目逼到这了,以后尽量避免这样的需求发生! CREATE OR REPLACE PROCEDURE i ...

  9. [Python Study Notes]饼状图绘制

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

  10. CUDA编程接口:异步并发执行的概念和API

    1.主机和设备间异步执行 为了易于使用主机和设备间的异步执行,一些函数是异步的:在设备完全完成任务前,控制已经返回给主机线程了.它们是: 内核发射; 设备间数据拷贝函数; 主机和设备内拷贝小于64KB ...