Java泛型的应用——T extends Comparable<? super T>
在观察Java源码的时候,发现了这么一个写法T extends Comparable<? super T>。不禁纳闷为什么要这么写呢?有什么好处吗,extends和super在这里的作用着实让人有点不清楚。
接下来,我将结合代码跟大家分享一下我关于这里泛型应用的看法。
1. <T extends Comparable<? super T>>代表什么意思
- 大家可以明白的是这里应用到了Java的泛型,那么首先向大家说明一下这里extends的作用
extends后面跟的类型,如<任意字符 extends 类/接口>表示泛型的上限。示例代码如下:
import java.util.*;
class Demo<T extends List>{}
public class Test
{
public static void main(String[] args) {
Demo<ArrayList> p = null; // 编译正确
//这里因为ArrayList是List的子类所以通过
//如果改为Demo<Collection> p = null;就会报错这样就限制了上限
}
}
- 在理解了extends所表示的泛型的上限后,接下来介绍一下super的作用,它与extends相反,表示的是泛型的下限。
- 所以结合上述两点,我们来分析一下这句话整体代表什么意思。首先,extends对泛型上限进行了限制即T必须是Comparable<? super T>的子类,然后<? super T>表示Comparable<>中的类型下限为T!
2. <T extends Comparable<T>> 和 <T extends Comparable<? super T>> 有什么不同
接下来我们通过对比,使得大家对为何要这样编写代码有更加深刻的印象。
<T extends Comparable<T>>
它代表的意思是:类型T必须实现Comparable接口,并且这个接口的类型是T。这样,T的实例之间才能相互比较大小。这边我们以Java中GregorianCalendar这个类为例。
代码如下所示:
import java.util.GregorianCalendar;
class Demo<T extends Comparable<T>>{}
//注意这里是没有? super的
public class Test
{
public static void main(String[] args) {
Demo<GregorianCalendar> p = null;
}
}
这里编译报错,因为这里的<T extends Comparable<T>>相当于<GregorianCalendar extends Comparable<GregorianCalendar>>,但是GregorianCalendar中并没有实现Comparable<GregorianCalendar>,而是仅仅持有从Calendar继承过来的Comparable<Calendar>,这样就会因为不在限制范围内而报错。
<T extends Comparable<? super T>>
它代表的意思是:类型T必须实现Comparable接口,并且这个接口的类型是T或者是T的任一父类。这样声明后,T的实例之间和T的父类的实例之间可以相互比较大小。同样还是以GregorianCalendar为例。代码如下所示:
import java.util.GregorianCalendar;
class Demo<T extends Comparable<? super T>>{}
public class Test1
{
public static void main(String[] args) {
Demo<GregorianCalendar> p = null; // 编译正确
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; public class Test
{
//第一种声明:简单,灵活性低
public static <T extends Comparable<T>> void mySort1(List<T> list)
{
Collections.sort(list);
} //第二种声明:复杂,灵活性高
public static <T extends Comparable<? super T>> void mySort2(List<T> l)
{
Collections.sort(list);
} public static void main(String[] args)
{
//主函数中将分别创建Animal和Dog两个序列,然后调用排序方法对其进行测试
//main函数中具体的两个版本代码将在下面具体展示
}
} class Animal implements Comparable<Animal>
{
protected int age; public Animal(int age) {
this.age = age;
} //使用年龄与另一实例比较大小
@Override
public int compareTo(Animal other)
{
return this.age - other.age;
}
} class Dog extends Animal
{
public Dog(int age)
{
super(age);
}
}
上面的代码包括三个类:
Animal实现了Comparable<Animal>接口,通过年龄来比较实例的大小- Dog从Animal继承,为其子类。
Test类中提供了两个排序方法和测试用的main()方法:mySort1()使用<T extends Comparable<T>>类型参数mySort2()使用<T extends Comparable<? super T>>类型参数main()测试方法。在这里将分别创建Animal和Dog两个序列,然后调用排序方法对其进行测试。
3.1 对mySort1()进行测试,main方法代码如下所示:
// 创建一个 Animal List
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal(25));
animals.add(new Dog(35)); // 创建一个 Dog List
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog(5));
dogs.add(new Dog(18)); // 测试 mySort1() 方法
mySort1(animals);
mySort1(dogs);
结果编译出错,报错信息为:
The method mySort1(List<T>) in the type TypeParameterTest is not applicable for the arguments (List<Dog>)
mySort1() 方法的类型参数是<T extends Comparable<T>>,它要求的类型参数是类型为T的Comparable。
如果传入的是List<Animal>程序将正常执行,因为Animal实现了接口Comparable<Animal>。
但是,如果传入的参数是List<Dog>程序将报错,因为Dog类中没有实现接口Comparable<Dog>,它只从Animal继承了一个Comparable<Animal>接口。
注意:animals list中实际上是包含一个Dog实例的。如果碰上类似的情况(子类list不能传入到一个方法中),可以考虑把子类实例放到一个父类 list 中,避免编译错误。
3.2 对mySort12()进行测试,main方法代码如下所示:
public static void main(String[] args)
{
// 创建一个 Animal List
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal(25));
animals.add(new Dog(35)); // 创建一个 Dog List
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog(5));
dogs.add(new Dog(18)); // 测试 mySort2() 方法
mySort2(animals);
mySort2(dogs);
}
这时候我们发现该程序可以正常运行。它不但能够接受Animal implements Comparable<Animal>这样的参数,也可以接收:Dog implements Comparable<Animal>这样的参数。
class Dog extends Animal implements Comparable<Dog>
{
public Dog(int age)
{
super(age);
}
}
结果程序编译报错,错误信息如下所示:
The interface Comparable cannot be implemented more than once with different arguments: Comparable<Animal> and Comparable<Dog>
意义是Dog类已经从Animal中继承了Comparable该接口,无法再实现一个Comparable。
若子类需要使用自己的比较方法,则需要重写父类的public int CompareTo(Animal other)方法。
4. 总结
对Animal/Dog这两个有父子关系的类来说:<T extends Comparable<? super T>>可以接受List<Animal>,也可以接收 List<Dog> 。而<T extends Comparable<T>>只可以接收 List<Animal>所以,<T extends Comparable<? super T>>这样的类型参数对所传入的参数限制更少,提高了 API 的灵活性。总的来说,在保证类型安全的前提下,要使用限制最少的类型参数。
Java泛型的应用——T extends Comparable<? super T>的更多相关文章
- Java范型之T extends Comparable<? super T>
在观察Java源码的时候,发现了这么一个写法T extends Comparable<? super T>.不禁纳闷为什么要这么写呢?有什么好处吗,extends和super在这里的作用着 ...
- Java泛型 PECS(Producer Extends, Consumer Super)
本文转载自ImportNew,原文链接 Java 泛型: 什么是PECS(Producer Extends, Consumer Super) PECS指“Producer Extends,Consum ...
- <T extends Comparable<? super T>>什么意思
<T extends Comparable<? super T>>首先这是运用了java的泛型①extends后面跟的类型如<任意字符 extends 类/接口>表 ...
- 如何理解 Java 中的 <T extends Comparable<? super T>>
Java 中类似 <T extends Comparable<? super T>> 这样的类型参数 (Type Parameter) 在 JDK 中或工具类方法中经常能看到. ...
- <T extends Comparable<? super T>>
在看Collections源代码中,看到如下代码: public static <T extends Comparable<? super T>> void sort(List ...
- 如何理解<T extends Comparable<? super T>>
在看java容器类的时候经常可以看到<T extends Comparable<? super T>>,感觉十分不解? 我们觉得<T extends Comparable ...
- 浅谈Java泛型中的? extends E和?super E
https://blog.csdn.net/zymx14/article/details/78073757
- java泛型中使用的排序算法——归并排序及分析
一.引言 我们知道,java中泛型排序使用归并排序或TimSort.归并排序以O(NlogN)最坏时间运行,下面我们分析归并排序过程及分析证明时间复杂度:也会简述为什么java选择归并排序作为泛型的排 ...
- Java 泛型和通配符解惑
转自:http://www.linuxidc.com/Linux/2013-10/90928.htm T 有类型 ? 未知类型 一.通配符的上界 既然知道List<Cat>并不是Lis ...
随机推荐
- 老李分享:天使投资 vs. 风险投资 vs. 私募股权融资
天使投资(Angel Capital) 创意阶段(idea stage)到种子阶段(seed stage) 0 – 1百万美元营业额 还没有盈利 小股东 风险异常的高 不存在负债情况 风险投资(Ven ...
- 接口测试培训:HTTP协议基础
接口测试培训:HTTP协议基础 引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展. ...
- 老李分享:Android性能优化之内存泄漏1
老李分享:Android性能优化之内存泄漏 前言 对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我 ...
- 锋利的jQuery(1)——DOM对象与jQuery对象的转换
1.认识jQuery DOM对象:即文档对象模型 jQuery对象:通过jQuery包装DOM对象后产生的对象.这是jQuery对象所特有的,在jQuery对象中无法使用DOM对象的任何方法,同时DO ...
- NDK 线程同步
使用场景 对底层代码进行 HOOK, 不可避免的要考虑多线程同步问题, 当然也可以写个类似 java 的线程本地变量来隔离内存空间. 死锁分析 恩, 道理其实大家都懂的, 毕竟大学就学了操作系统,理论 ...
- Twitter数据非API采集方法
说明:这里分三个系列介绍Twitter数据的非API抓取方法. 在一个老外的博看上看到的,想详细了解的可以自己去看原文. 这种方法可以采集基于关键字在twitter上搜索的结果推文,已经实现自动翻页功 ...
- 转账示例(三):service层面实现(线程管理Connection)(本例采用QueryRunner来执行sql语句,数据源为C3P0)
缺点:Service层面还是不应该出现关于事务的操作1.自行创建C3P0Uti,account数据库,导入Jar包 2.Dao层面 接口: package com.learning.dao; impo ...
- three.js提供的几何体
1.简单几何体 three.js提供的稍微简单点的几何体包括有:PlaneGeometry(平面).CircleGeometry(圆形).ShapeGeometry(塑性).CubeGeometry( ...
- xmlplus 组件设计系列之零 - xmlplus 简介
xmlplus 是什么 xmlplus 是博主写的一个 JavaScript 框架,用于快速开发前后端项目. xmlplus 基于组件设计,组件是基本的构造块.评价组件设计好坏的一个重要标准是封装度. ...
- @JsonIgnoreProperties注解不起作用的问题解决
最近做的一个东西要调第三方服务接口,要参照接口文档开发,但是第三方服务的接口字段名全部都是大写,本来以为这种应该没有什么问题.但是实际开发中发现大写的字段名字去调后台接口的时候报: org.codeh ...