java泛型通配符

【0】README

0.1)以下内容转自: http://blog.csdn.net/baple/article/details/25056169
0.2) T 有类型 + ? 未知类型


一、通配符的上界(extends关键字)

  • 既然知道List并不是List的子类型,那就需要去寻找替他解决的办法, 是AnimalTrianer.act()方法变得更为通用(既可以接受List类型,也可以接受List等参数)。在java里解决办法就是使用通配符“?”,具体到AnimalTrianer,就是将方法改为act(List<? extends Animal> list),当中“?”就是通配符,而“? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”可以代表Animal或其子类,可代表不了Animal的父类(如Object),因为通配符的上界是Animal。
  • 如下,为改进之后的AnimalTrianer
publicclass AnimalTrainer {publicvoid act(List<? extends Animal> list) {for (Animal animal : list) {animal.eat();}}
}
  • 再来测试一下,如下,发现Test 2 可以通过编译了:
publicclass TestAnimal {publicstaticvoid main(String[] args) {AnimalTrainer animalTrainer = new AnimalTrainer();//Test 1List<Animal> animalList = new ArrayList<>();animalList.add(new Cat("cat1"));animalList.add(new Bird("bird1"));animalTrainer.act(animalList);  //可以通过编译//Test 2List<Cat> catList = new ArrayList<>();catList.add(new Cat("cat2"));catList.add(new Cat("cat3"));animalTrainer.act(catList);     //也可以通过编译}
}
  • 经过上述分析,可以知道List< Animal>和List< Cat>都是List< ? extends Animal>的子类型,类似有List< Bird>,List< Magpie>也是List< ? extends Animal>的子类型。

Conclusion)现总结如下,对于通配符的上界,有以下几条基本规则:(假设给定的泛型类型为G,(如List中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))(干货)

  • C1) G< ? extends Y> 是 G< ? extends X>的子类型(如List< ? extends Cat> 或 List< Animal> 是 List< ? extends Animal>的子类型)。
  • C2) G< X> 是 G< ? extends X>的子类型(如List< Animal> 是 List< ? extends Animal>的子类型)
  • C3) G< ?> 与 G< ? extends Object>等同,如List< ?> 与List< ? extends Object>等同。

  • 学到这里,可能会遇到一些疑惑的地方,或者说事理解不透的地方,先观察如下两段代码片段,判断一下其是否可行??

//"代码1"
public void testAdd(List<? extends Animal> list){//....其他逻辑list.add(new Animal("animal"));list.add(new Bird("bird"));list.add(new Cat("cat"));}
//"代码2"
List<? extends Animal> list = new ArrayList<>();
list.add(new Animal("animal"));
list.add(new Bird("bird"));
list.add(new Cat("cat"));

对以上代码的分析(Analysis): 因为“? extends Animal”可代表Animal或其子类(Bird,Cat),那上面的操作应该是可行的。事实上是”不行“,即无法通过编译。为什么呢??

  • A1)在解释之前,再来重新强调一下已经知道的规则:在List< Animal> list里只能添加Animal类对象及其子类对象(如Cat和Bird对象),在List< Bird>里只能添加Bird类和其子类对象(如Magpie),可不能添加Animal对象(不是Bird的子类),类似的在List< Cat>和List< Magpie>里只能添加Cat和Bird对象(或其子类对象,不过这没有列出)。
  • A2)现在再回头看一下testAdd()方法: 我们知道List< Animal>、List< Cat>等都是List<? extends Animal>的子类型。先假设传入的参数为为List< Animal>,则第一段代码的三个“add”操作都是可行的;可如果是List< Bird>呢??则只有第二个“add”可以执行;再假设传入的是List< Tiger>(Tiger是想象出来的,可认为是Cat的子类),则三个“add”操作都不能执行。
  • A3)现在反过来说:给testAdd传入不同的参数,三个“add”操作都可能引发类型不兼容问题,而传入的参数是未知的,所以java为了保护其类型一致,禁止向List< ? extends Animal>添加任意对象,不过却可以添加null,即list.add(null)是可行的。
  • A4)有了上面谈到的基础,再来理解第二段代码就不难了: 因为List< ? extends Animal>的类型“? extends Animal”无法确定,可以是Animal,Bird或者Cat等,所以为了保护其类型的一致性,也是不能往list添加任意对象的,不过却可以添加null。

先总结如下:不能往List< ? extends Animal> 添加任意对象,除了null。

  • 另外提醒大家注意的一点是:在List< ? extends Animal> 可以是Animal类对象或Bird对象等(只是某一类对象),反过来说,在List< ? extends Animal> list里的都是Animal对象,即Bird也是Animal对象,Cat也是Animal对象(用java的语言来说就是子类可以指向父类,父类却不能指向子类),那么在Animal里的所有方法都是可以调用的,如下:
    for (Animal animal : list) { animal.eat(); }

二、通配符的下界(super关键字)

  • 既然有了通配符的上界,自然有着通配符的下界。可以如此定义通配符的下界 List< ? super Bird>,其中”Bird“就是通配符的下界。
  • Attention)注意:不能同时声明泛型通配符申明上界和下界。
  • 1)在谈注意细节之前,我们先看一下通配符的使用规则——对于通配符的上界,有以下几条基本规则:(假设给定的泛型类型为G,(如List中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))
    G< ? super X> 是 G< ? super Y>的子类型(如List< ? super Animal> 是 List< ? super Bird>的子类型)。(干货——List< ? super Animal> 是 List< ? super Bird>的子类型,其中Bird extends Animal)
    G< X> 是 G< ? super X>的子类型(如List< Animal> 是 List< ? super Animal>的子类型)
    (干货——List< Animal> 是 List< ? super Animal>的子类型)
  • 2)现在再来看如下代码,判断其是否符合逻辑:
//"代码1"
public void testAdd(List<? super Bird> list){list.add(new Bird("bird"));list.add(new Magpie("magpie"));}//"代码2"
List<? super Bird> list = new ArrayList<>();
list.add(new Bird("bird"));
list.add(new Magpie("magpie"));
list.add(new Animal("animal"));

对以上代码的分析(Analysis)

  • A1)看第一段代码,其分析如下:因为”? super Bird”代表了Bird或其父类,而Magpie是Bird的子类,所以上诉代码不可通过编译。而事实上是”行“,为什么呢?2?
  • A2)在解疑之前,再来强调一个知识点,子类可以指向父类,即Bird也是Animal对象。现在考虑传入到testAdd()的所有可能的参数,可以是List,List,或者List等等,发现这些参数的类型是Bird或其父类,那我们可以这样看,把bird、magpie看成Bird对象,也可以将bird、magpie看成Animal对象,类似的可看成Object对象,最后发现这些添加到List< ? supe Bird> list里的对象都是同一类对象(如本文刚开篇提到的Test 1),因此testAdd方法是符合逻辑,可以通过编译的。:
  • A3)现在再来看一下第二段代码: 对于,第二、三行代码的解释和上文一样,至于最后一行“list.add(newAnimal(“animal”))”是无法通过编译的,为什么的??为了保护类型的一致性,因为“? super Bird”可以是Animal,也可以是Object或其他Bird的父类,因无法确定其类型,也就不能往List< ? super Bird>添加Bird的任意父类对象。
    • 既然无法确定其父类对象,那该如何遍历List< ? super Bird> ? 因为Object是所有类的根类,所以可以用Object来遍历。如下,不过貌似其意义不大。
      for (Object object : list) {//…}
  • A4)那“? super BoundingType”可以应用在什么地方呢??“? super BoundingType”应用相对广泛,只不过是混合着用。下面举个简单的例子。先假设有以下两个Student和CollegeStudent,当中CollegeStudent继承Student,如下:
    (干货荔枝)
public class Student implements Comparable<Student>{privateint id;public Student(int id) {this.id = id;}@Overridepublicint compareTo(Student o) {return (id > o.id) ? 1 : ((id < o.id) ? -1 : 0);}
} 
publicclass CollegeStudent extends Student{public CollegeStudent(int id) {super(id);}
}
  • 先需要根据他们的id对他们进行排序(注意此处是对数组对象进行排序),设计方法如下,(n指数组元素的个数):
public static <T extends Comparable<? super T>> void selectionSort(T[] a,int n)
  • 先理解此方法含义,首先< T extends Comparable< T>>规定了数组中对象必须实现Comparable接口,Comparable< ? Super T>表示如果父类实现Comparable接口,其自身可不实现,如CollegeStudent。先假设有一个CollegeStudent的数组,如下:
CollegeStudent[] stu = new CollegeStudent[]{new CollegeStudent(3),new CollegeStudent(2),new CollegeStudent(5),new CollegeStudent(4)};
  • 执行方法 selectionSort(stu,4)是完全可以通过的。可如果定义的selectionSort方法如下:
public static <T extends Comparable<T>> void selectionSort(T[] a,int n)
  • 则方法selectionSort(stu,4)不能执行,因为CollegeStudent没有实现Comparable< CollegeStudent>接口。换句话就是“? super T”使selectionSort方法变得更为通用了。 (源代码参见文末)
    这里写图片描述

三、无界通配符

1)知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰即可,单独一个“?”。如List< ?>,“?”可以代表任意类型,“任意”也就是未知类型。
2)无界通配符通常会用在下面两种情况: (干货——无界通配符通常会用在下面两种情况)

  • 2.1)当方法是使用原始的Object类型作为参数时,如下:
public static void printList(List<Object> list) {for (Object elem : list)System.out.println(elem + "");System.out.println();
}
  • 可以选择改为如下实现:
publicstaticvoid printList(List<?> list) {for (Object elem: list)System.out.print(elem + "");System.out.println();
}
  • 这样就可以兼容更多的输出,而不单纯是List,如下:
List<Integer> li = Arrays.asList(1, 2, 3);
List<String>  ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);
  • 2.2)在定义的方法体的业务逻辑与泛型类型无关:如List.size,List.cleat。实际上,最常用的就是Class< ?>,因为Class< T>并没有依赖于T。(干货——这里又用到了java的反射机制)
  • Attention)最后提醒一下的就是: List< Object>与List< ?>并不等同,List< Object>是List< ?>的子类。还有不能往List< ?> list里添加任意对象,除了null。

【4】 source code at a glance

(https://github.com/pacosonTang/core-java-volume/blob/master/chapter12/StudentTest.java)

package com.corejava.chapter12_4;import static java.lang.System.*;
public class StudentTest
{public static void main(String[] args){CollegeStudent[] stu = new CollegeStudent[]{new CollegeStudent(3),new CollegeStudent(2),new CollegeStudent(5),new CollegeStudent(4)};sortWithoutWildCard(stu);for(CollegeStudent cs : stu){out.println(cs.toString());}}// take insertion sort by id from small to larger order// T=CollegeStudent,然而CollegeStudent并没有继承Comparable<CollegeStudent>,// 仅仅是继承了Comparable<Student>,且Comparable<Student> 与 Comparable<CollegeStudent> 并没有任何关系// 即使 CollegeStudent extends Student// 但是这里并没有报错,不觉明里(理论上是说不通的,但实际上却没有报错)public static <T extends Comparable<T>> void sortWithoutWildCard(T[] a){       // CompaT temp;int j;for (int i = 1; i < a.length; i++){temp = a[i];for (j = i; j > 0 && a[j-1].compareTo(temp) > 0; j--){a[j] = a[j-1];}a[j] = temp;}}// 这里T=CollegeStudent,理论上是说的通的(实际编译也可以通过)// 因为这里的泛型方法的类型限定为 <T extends Comparable<? super T>>// 而 Comparable<Student> 是  Comparable<? super T> 的子类,且CollegeStudent extends Comparable<Student>public static <T extends Comparable<? super T>> void sortWithWildCard(T[] a){T temp;int j;for (int i = 1; i < a.length; i++){temp = a[i];for (j = i; j > 0 && a[j-1].compareTo(temp) > 0; j--){a[j] = a[j-1];}a[j] = temp;}}
}class Student implements Comparable<Student>
{private int id;public Student(int id){this.id = id;}@Overridepublic int compareTo(Student o){return this.id - o.id;}@Overridepublic String toString(){return id + " ";}
}class CollegeStudent extends Student //implements Comparable<CollegeStudent>
{public CollegeStudent(int id){super(id);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jdk 1.8 内存可见性_JDK 14中的常规,安全和确定性外部内存访问

jdk 1.8 内存可见性在“ JDK 14 Rampdown&#xff1a;Build 27 ”一文中&#xff0c;我总结了JDK 14 Early Access Build &#xff03;27中新增的许多针对JDK 14的功能。 已经存在另一种JDK 14 Early Access Build&#xff0c;并且此[ Build 28&#xff08;2019/12/18&#xff…

linux开启防火墙ping,如何在防火墙中放开ping

如何在防火墙中放开ping操作时需要注意的几项:1,注意iptables各版本间的区别我们的server os最旧的版本是redhat 7.3 kernel是2.4.20-18.7最新的server os最新的版本是centos 5, kernel是2.6.18-8差距很大iptables以模块形式运行在内核的空间,用lsmod可以看到所以它与内核的版本…

Hibernate: You have an error in your SQL syntax; check the manual that corresponds to your MySQL

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ...Hibernate 出现这样的错误是因为表中的字段名使用了sql的保留字&#xff0c;不要使用保留字 一、mysql的保留字主要有&#xff1…

java.io 包

java.io 通过数据流、序列化和文件系统提供系统输入和输出。 请参见&#xff1a; 接口摘要CloseableCloseable 是可以关闭的数据源或目标。DataInputDataInput 接口用于从二进制流中读取字节&#xff0c;并根据所有 Java 基本类型数据进行重构。DataOutputDataOutput 接口用…

linux系统怎么安装pr,Linux安装后的配置

系统:CentOS-6.31.NVIDIA驱动的安装与配置Linux本身的显卡驱动是能够满足平常应用的&#xff0c;但是有时候我们为了获得3D效果&#xff0c;或者更流畅的视频体验&#xff0c;我们会安装新的显卡驱动。这时候就需要安装闭源驱动了&#xff0c;个人认为闭源的驱动更为好一些&…

java第三阶段源代码_有效Java第三版的源代码已更新为使用较新的功能

java第三阶段源代码那些已经阅读了有效Java 第三版的人可能知道与该书相关的源代码可以在GitHub上获得 。 jbloch / effective-java-3e-source-code项目拥有1700多个星星&#xff0c;截至撰写本文时&#xff0c;它已被分叉了近800次。 在有效Java的第三版中&#xff0c; Java的…

《线性代数及其应用》

【0】README 0.1&#xff09;以下内容转自&#xff1a; http://blog.csdn.net/ljbkiss/article/details/7194719 【1】正文干货 1.1&#xff09; 断断续续的终于把 《线性代数及其应用&#xff08;Linear Algebra and Its Application&#xff09; David C.Lay》 这本书看完了…

micrometer_具有InlfuxDB的Spring Boot和Micrometer第1部分:基础项目

micrometer对于那些关注此博客的人来说&#xff0c;难怪我会经常使用InfluxDB。 我喜欢这样一个事实&#xff0c;它是一个真正的单一用途的数据库&#xff08;时间序列&#xff09;&#xff0c;具有许多功能&#xff0c;并且还带有企业支持。 Spring也是我选择的工具之一。 因…

如何在win7(xp)home version下安装 rose 32 bit

【0】README 0.1&#xff09; 以下部分内容转自 http://blog.csdn.net/encienqi/article/details/5578725 【1】 干货开始 如果是家庭版(win7 or WindowsXPHomeEdition)请先按以下步骤修改部分文件后再安装&#xff1a; 1.1&#xff09;安装Microsoft Orca工具 step1&#x…

接口 Closeable

java.io 接口 Closeable public interface Closeable Closeable 是可以关闭的数据源或目标。调用 close 方法可释放对象保存的资源&#xff08;如打开文件&#xff09;。 从以下版本开始&#xff1a;1.5方法摘要 void close() 关闭此流并释放与此流关联的所有系统资源。 方法详…

Linux下python包放在哪,在alpinlinux中使用apk安装的Python包

我想用apk在Alpine Linux中安装一些Python包。我用numpy作为下面的例子。文档文件FROM python:3-alpineRUN apk add --update py3-numpy我建立我的码头形象$ docker build -t python-numpy .Sending build context to Docker daemon 2.048kBStep 1/2 : FROM python:3-alpine---…

spring 消息传递机制_Spring再次涵盖了您:继续进行消费者驱动的消息传递合同测试...

spring 消息传递机制在上一篇文章中&#xff0c;我们已经开始讨论基于消息的通信中的消费者驱动的合同测试 。 在今天的帖子中&#xff0c;我们将在测试工具箱中包含另一个工具&#xff0c;但是在此之前&#xff0c;让我对显微镜下的系统进行快速回顾。 它有两项服务&#xff0…

接口 DataInput

java.io接口 DataInput 所有已知子接口&#xff1a;ImageInputStream, ImageOutputStream, ObjectInput所有已知实现类&#xff1a;DataInputStream, FileCacheImageInputStream, FileCacheImageOutputStream, FileImageInputStream, FileImageOutputStream, ImageInputStreamI…

java反射机制总结

【0】README 0.1&#xff09;以下内容转自&#xff1a; http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html 0.2&#xff09;for source code, please visit &#xff08;1~14 source code : https://github.com/pacosonTang/core-java-volume/blob/master…

c语言系统时间的作用,C语言得到当前系统时间

void getTime(){//获取当前系统时间time_t tTime;//距离1900年1月1日的秒数char str[80];struct tm* stTim;//时间结构time(&tTime);stTim localtime(&tTime);strftime(str,sizeof(str),"%Y%m%d%T",stTim);//格式化时间printf(str);}格式化时间的格式%a 星期…

aws集群重启_使用自动伸缩组在AWS中运行安全数据库集群

aws集群重启当您必须在AWS上运行可扩展应用程序时&#xff0c;您的数据库也必须是可扩展的。 扩展无状态应用程序层更容易&#xff0c;在无状态应用程序层中&#xff0c;每个节点几乎都是一次性的-即使3节点集群中的一个节点发生故障&#xff0c;您也可以启动另一个节点&#x…

接口 DataOutput

java.io 接口 DataOutput 所有已知子接口&#xff1a; ImageOutputStream, ObjectOutput 所有已知实现类&#xff1a; DataOutputStream, FileCacheImageOutputStream, FileImageOutputStream, ImageOutputStreamImpl, MemoryCacheImageOutputStream, ObjectOutputStream, Rand…

java流与文件——流

【0】README 0.1&#xff09; 本文描述转自 core java volume 2&#xff0c; 旨在理解 java流与文件——流 的相关知识&#xff1b; 0.2&#xff09; 输入流和输出流&#xff08;InputStream 和 OutputStream传输单位是基于单字节&#xff09;&#xff1a; 0.2.1&#xff09…

c语言结构体定义字符串数组,C语言,结构体中字符串的声明(采用字符指针还是字符数组)...

结构体中&#xff0c;字符串选项是用字符数组表示好&#xff0c;还是用字符指针表示好&#xff1f;typedef struct person{char *name;int age;char sex[6];}&#xff1b;该结构体中name用的是指针而不是数组&#xff0c;所以需要给字符串在堆上申请内存然后再赋值&#xff1b;…

java –cp ./:_成为Java流专家–第2部分:中级操作

java –cp ./:就像魔术棒一样&#xff0c;中间操作将一个Stream转换为另一个Stream。 这些操作可以无穷无尽的方式组合在一起&#xff0c;以可读有效的方式执行从简单到高度复杂的任务。 本文是五分之二&#xff0c;其中还有一个GitHub存储库&#xff0c;其中包含每个单元的说…