在观察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; // 编译正确
}
}  
  此时编译通过,这里可以理解为<GregorianCalendar extends Comparable<Calendar>>!因为Calendar为GregorianCalendar 的父类并且GregorianCalendar 实现了Comparable<Calendar>,具体可以在API中进行查看!
 
  3. 实例代码演示
  代码如下所示:
 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);
}
}

上面的代码包括三个类:

  1. Animal实现了Comparable<Animal>接口,通过年龄来比较实例的大小
  2. Dog从Animal继承,为其子类。
  3. 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>这样的参数。

  3.3 是否可以通过将Dog实现Comparable<Dog>来解决问题?
  由分析可得程序出现问题是因为Dog类没有实现接口Comparable<Dog>,那么我们能否将该类实现接口Comparable<Dog>来解决问题呢?
  代码如下所示:
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>的更多相关文章

  1. Java范型之T extends Comparable<? super T>

    在观察Java源码的时候,发现了这么一个写法T extends Comparable<? super T>.不禁纳闷为什么要这么写呢?有什么好处吗,extends和super在这里的作用着 ...

  2. Java泛型 PECS(Producer Extends, Consumer Super)

    本文转载自ImportNew,原文链接 Java 泛型: 什么是PECS(Producer Extends, Consumer Super) PECS指“Producer Extends,Consum ...

  3. <T extends Comparable<? super T>>什么意思

    <T extends Comparable<? super T>>首先这是运用了java的泛型①extends后面跟的类型如<任意字符 extends 类/接口>表 ...

  4. 如何理解 Java 中的 <T extends Comparable<? super T>>

    Java 中类似 <T extends Comparable<? super T>> 这样的类型参数 (Type Parameter) 在 JDK 中或工具类方法中经常能看到. ...

  5. <T extends Comparable<? super T>>

    在看Collections源代码中,看到如下代码: public static <T extends Comparable<? super T>> void sort(List ...

  6. 如何理解<T extends Comparable<? super T>>

    在看java容器类的时候经常可以看到<T extends Comparable<? super T>>,感觉十分不解? 我们觉得<T extends Comparable ...

  7. 浅谈Java泛型中的? extends E和?super E

    https://blog.csdn.net/zymx14/article/details/78073757

  8. java泛型中使用的排序算法——归并排序及分析

    一.引言 我们知道,java中泛型排序使用归并排序或TimSort.归并排序以O(NlogN)最坏时间运行,下面我们分析归并排序过程及分析证明时间复杂度:也会简述为什么java选择归并排序作为泛型的排 ...

  9. Java 泛型和通配符解惑

    转自:http://www.linuxidc.com/Linux/2013-10/90928.htm T  有类型 ?  未知类型 一.通配符的上界 既然知道List<Cat>并不是Lis ...

随机推荐

  1. Java面向对象核心技能

    1.封装 封装是面向对象的三大特性之一,就是将类的状态信息隐藏在类内部,不允许外部程序直接访问,而通过该类提供的方法来实现对隐藏信息的操作和访问. 封装的好处:隐藏类的实现细节:让使用者只能通过程序规 ...

  2. (转)POPTEST创始人李爱然:谢谢,帮助我的朋友!!!!

    2015年11月15日,脉脉上随意浏览信息.每天有很多人加我,我也会主动加一些人.脉脉的广告语“打通职场人脉”,很直白的告诉我们脉脉是用来找人办事的.简单明了,不用故作清高. “利”,有利可图便是“友 ...

  3. canvas画布

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  4. Android IPC机制全解析<二>

    在AIDL文件中并不是所有的数据类型都可以使用,AIDL支持的数据类型如下: 基本数据类型(int.long.char.boolean.double等) String和CharSequence Lis ...

  5. scss的初级学习随笔小计

    $white: #fff;$three: #333;$six: #666;$nine: #999;$red: #fff;$orange: #f63;$yellow: #fc0;$opcity: rgb ...

  6. Visual Studio中的TabControl控件的用法

    今天遇到了一个自己没遇到过的控件TabControl控件,所以找了点关于它的资料 TabControl属性 DisplayRect:只定该控件客户区的一个矩形  HotTrack:设置当鼠标经过页标签 ...

  7. 【学而思】利用shouldComponentUpdate钩子函数优化react性能以及引入immutable库的必要性

    凡是参阅过react官方英文文档的童鞋大体上都能知道对于一个组件来说,其state的改变(调用this.setState()方法)以及从父组件接受的props发生变化时,会导致组件重渲染,正所谓&qu ...

  8. Robotframe work学习之初(二)

    一.F5帮助 Robot Framework 并没有像其它框架一样提供一份完整的 API 文档,所以,我们没办法通过官方 API文档进行习.RIDE 提供了 F5 快捷键来打开帮助文档. search ...

  9. 【shell编程基础3】shell编程的组合应用之二:管道及其命令

    预备知识: 管道:它是一个单向的,可以把前一个的数据输出导向到下一个命令的工具,这样可以实现多个命令组合处理一套数据. 它的符号是  "|"    管道只能处理经过前面一个命令传过 ...

  10. 串口屏与触摸屏人机界面组态软件HMIMaker介绍

    串口屏与触摸屏人机界面组态软件HMIMaker介绍 触摸屏人机界面组态软件HMIMaker,是一款基于ARM架构的嵌入式控制系统开发的嵌入式软件,专业应用于触摸屏的二级界面开发,具有单片机协议,mod ...