zoukankan      html  css  js  c++  java
  • 如何理解 Java 中的 <T extends Comparable<? super T>>

    <T extends Comparable<T>> 和 <T extends Comparable<? super T>> 有什么不同

    <T extends Comparable<T>>
    类型 T 必须实现 Comparable 接口,并且这个接口的类型是 T。只有这样,T 的实例之间才能相互比较大小。例如,在实际调用时若使用的具体类是 Dog,那么 Dog 必须 implements Comparable<Dog>
    <T extends Comparable<? super T>>
    类型 T 必须实现 Comparable 接口,并且这个接口的类型是 T 或 T 的任一父类。这样声明后,T 的实例之间,T 的实例和它的父类的实例之间,可以相互比较大小。例如,在实际调用时若使用的具体类是 Dog (假设 Dog 有一个父类 Animal),Dog 可以从 Animal 那里继承 Comparable<Animal> ,或者自己 implements Comparable<Dog> 。

    2 我对 <T extends Comparable<? super T>> 类型参数的理解

    光看上面的定义除了摸不着头脑,不会有其它感觉。下面用代码来说明为什么要这样声明。

    2.1 代码运行环境

    我使用的 JDK 版本是: 1.8.0_60 ,在 Eclipse 中编译运行。因为注释用了中文,编码采用 UTF-8。如果你要在命令行下编译、运行,编译时要使用 -encoding UTF-8 选项:

    javac -encoding UTF-8 TypeParameterTest.java
    

    另外,Eclipse 中的警告、错误信息跟命令行中的不一样(个人感觉 Eclipse 中的信息要好懂一些)。以下的示例以 Eclipse 中的信息为准。

    2.2 示例代码

     1: package generics3;
     2: 
     3: import java.util.ArrayList;
     4: import java.util.Collections;
     5: import java.util.List;
     6: 
     7: public class TypeParameterTest
     8: {
     9:     //第一种声明:简单,灵活性低
    10:     public static <T extends Comparable<T>> void mySort1(List<T> list)
    11:     {
    12:         Collections.sort(list);
    13:     }
    14: 
    15:     //第二种声明:复杂,灵活性高
    16:     public static <T extends Comparable<? super T>> void mySort2(List<T> list)
    17:     {
    18:         Collections.sort(list);
    19:     }
    20: 
    21:     public static void main(String[] args)
    22:     {
    23:         //在这个方法中要创建一个 Animal List 和一个 Dog List,然后分别调用两个排序方法。
    24:     }
    25: }
    26: 
    27: class Animal implements Comparable<Animal>
    28: {
    29:     protected int age;
    30: 
    31:     public Animal(int age)
    32: 
    33:     {
    34:         this.age = age;
    35:     }
    36: 
    37:     //使用年龄与另一实例比较大小
    38:     @Override
    39:     public int compareTo(Animal other)
    40:     {
    41:         return this.age - other.age;
    42:     }
    43: }
    44: 
    45: class Dog extends Animal
    46: {
    47:     public Dog(int age)
    48:     {
    49:         super(age);
    50:     }
    51: }
    

    上面的代码包括三个类:

    1. Animal 实现了 Comparable<Animal> 接口,通过年龄来比较实例的大小
    2. Dog 继承自 Animal 。
    3. TypeParameterTest 类中提供了两个排序方法和测试用的 main() 方法:
      • mySort1() 使用 <T extends Comparable<T>> 类型参数
      • mySort2() 使用 <T extends Comparable<? super T>> 类型参数
      • main() 测试方法。在这个方法中要创建一个 Animal List 和一个 Dog List ,然后分别调用两个排序方法

    2.3 测试 mySort1() 方法

     1: // 创建一个 Animal List
     2: List<Animal> animals = new ArrayList<Animal>();
     3: animals.add(new Animal(25));
     4: animals.add(new Dog(35));
     5: 
     6: // 创建一个 Dog List
     7: List<Dog> dogs = new ArrayList<Dog>();
     8: dogs.add(new Dog(5));
     9: dogs.add(new Dog(18));
    10: 
    11: // 测试  mySort1() 方法
    12: mySort1(animals);
    13: mySort1(dogs);
    

    Line 13 出编译错误了。Eclipse 说:

    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 implements Comparable<Animal> 。

    但是,如果传入的参数是 List<Dog> 有问题,因为 Dog 没有 implements Comparable<Dog> ,它只是从 Animal 继承了一个 Comparable<Animal> 。

    不知道大家注意到没有,那个 animals list 中实际上是包含一个 Dog 实例的。如果你碰上类似的情况(子类 list 不能传入到一个方法中),可以考虑把子类实例放到一个父类 List 中,避免编译错误。

    2.4 测试 mySort2() 方法

     1: public static void main(String[] args)
     2: {
     3:     // 创建一个 Animal List
     4:     List<Animal> animals = new ArrayList<Animal>();
     5:     animals.add(new Animal(25));
     6:     animals.add(new Dog(35));
     7: 
     8:     // 创建一个 Dog List
     9:     List<Dog> dogs = new ArrayList<Dog>();
    10:     dogs.add(new Dog(5));
    11:     dogs.add(new Dog(18));
    12: 
    13:     // 测试  mySort2() 方法
    14:     mySort2(animals);
    15:     mySort2(dogs);
    16: }
    

    两个方法调用都没有问题。 第二个方法不但可以接受 Animal implements Comparable<Animal> 这样的参数,也可以接收: Dog implements Comparable<Animal> 这样的参数。

    2.5 Dog 可以 implements Comparable<Dog> 吗?

    如果让 Dog implements Comparable<Dog> 不也可以解决前面的那个编译错误吗?

    1: class Dog extends Animal implements Comparable<Dog>
    2:  {
    3:      public Dog(int age)
    4:      {
    5:          super(age);
    6:      }
    7:  }
    

    很不幸,出错了。Eclipse 说:

    The interface Comparable cannot be implemented more than once with different arguments: Comparable<Animal> and Comparable<Dog>
    

    就是说,Dog 已经从父类 Animal 那里继承了一个 Comparable ,它不能再实现一个 Comparable 。

    如果子类不喜欢父类的实现怎么办? Override 父类的 public int compareTo(Animal other) 方法。

    2.6 <T extends Comparable<? super T>> 类型参数声明的好处

    对 Animal/Dog 这两个有父子关系的类来说: <T extends Comparable<? super T>> 可以接受 List<Animal> ,也可以接收 List<Dog> 。 而 <T extends Comparable<T>> 只可以接收 List<Animal>

    所以,<T extends Comparable<? super T>> 这样的类型参数对所传入的参数限制更少,提高了 API 的灵活性。总的来说,在保证类型安全的前提下,要使用限制最少的类型参数。  

  • 相关阅读:
    前端编程规范记录
    搬砖工坑爹教程
    JS的模块化编程学习之旅
    后端开发遇到的问题
    git学习中遇到的疑难杂症
    微信小程序填坑之旅
    详解Redis中两种持久化机制RDB和AOF
    redis系列:RDB持久化与AOF持久化
    Java中判断字符串是否为数字
    @Aspect 注解使用详解
  • 原文地址:https://www.cnblogs.com/zquan/p/9374721.html
Copyright © 2011-2022 走看看