• Java-泛型编程-使用通配符? extends 和 ? super


    原文地址:http://blog.csdn.net/fw0124/article/details/42296283

    泛型中使用通配符有两种形式:子类型限定<? extends xxx>和超类型限定<? super xxx>。

    (1)子类型限定

    下面的代码定义了一个Pair<T>类,以及Employee,Manager和President类。

    1. public class Pair<T> {  
    2.     private T first;  
    3.     private T second;  
    4.   
    5.     public Pair(T first, T second) {  
    6.         this.first = first;  
    7.         this.second = second;  
    8.     }  
    9.   
    10.     public T getFirst() {  
    11.         return first;  
    12.     }  
    13.   
    14.     public T getSecond() {  
    15.         return second;  
    16.     }  
    17.   
    18.     public void setFirst(T newValue) {  
    19.         first = newValue;  
    20.     }  
    21.   
    22.     public void setSecond(T newValue) {  
    23.         second = newValue;  
    24.     }  
    25. }  
    26.   
    27. class Employee {  
    28.     private String name;  
    29.     private double salary;  
    30.       
    31.     public Employee(String n, double s) {  
    32.         name = n;  
    33.         salary = s;  
    34.     }  
    35.       
    36.     public String getName() {  
    37.         return name;  
    38.     }  
    39.   
    40.     public double getSalary() {  
    41.         return salary;  
    42.     }  
    43. }  
    44.   
    45. class Manager extends Employee {  
    46.     public Manager(String n, double s) {  
    47.         super(n, s);  
    48.     }  
    49. }  
    50. <pre name="code" class="java">  
    51. class President extends Manager {  
    52.     public President(String n, double s) {  
    53.         super(n, s);  
    54.     }  
    55. }  
    public class Pair<T> {
        private T first;
        private T second;
    
        public Pair(T first, T second) {
            this.first = first;
            this.second = second;
        }
    
        public T getFirst() {
            return first;
        }
    
        public T getSecond() {
            return second;
        }
    
        public void setFirst(T newValue) {
            first = newValue;
        }
    
        public void setSecond(T newValue) {
            second = newValue;
        }
    }
    
    class Employee {
        private String name;
        private double salary;
        
        public Employee(String n, double s) {
            name = n;
            salary = s;
        }
        
        public String getName() {
            return name;
        }
    
        public double getSalary() {
            return salary;
        }
    }
    
    class Manager extends Employee {
        public Manager(String n, double s) {
            super(n, s);
        }
    }
    <pre name="code" class="java">
    class President extends Manager {
        public President(String n, double s) {
            super(n, s);
        }
    }
    
    
    
    

    现在要定义一个函数可以打印Pair<Employee>

    1. public static void printEmployeeBoddies(Pair<Employee> pair) {  
    2.     System.out.println(pair.getFirst().getName() + ":" + pair.getSecond().getName());  
    3. }  
        public static void printEmployeeBoddies(Pair<Employee> pair) {
            System.out.println(pair.getFirst().getName() + ":" + pair.getSecond().getName());
        }
    

    可是有一个问题是这个函数输入参数只能传递类型Pair<Employee>,而不能传递Pair<Manager>和Pair<President>。例如下面的代码会产生编译错误

    1. Manager mgr1 = new Manager("Jack", 10000.99);  
    2. Manager mgr2 = new Manager("Tom", 10001.01);  
    3. Pair<Manager> managerPair = new Pair<Manager>(mgr1, mgr2);  
    4. PairAlg.printEmployeeBoddies(managerPair);  
            Manager mgr1 = new Manager("Jack", 10000.99);
            Manager mgr2 = new Manager("Tom", 10001.01);
            Pair<Manager> managerPair = new Pair<Manager>(mgr1, mgr2);
            PairAlg.printEmployeeBoddies(managerPair);

    之所以会产生编译错误,是因为Pair<Employee>和Pair<Manager>实际上是两种类型。

    由上图可以看出,类型Pair<Manager>是类型Pair<? extends Employee>的子类型,所以为了解决这个问题可以把函数定义改成 public static void printEmployeeBoddies(Pair<? extends Employee> pair)

    但是使用通配符会不会导致通过Pair<? extends Employee>的引用破坏Pair<Manager>对象呢?例如: Pair<? extends Employee> employeePair = managePair;employeePair.setFirst(new Employee("Tony", 100)); 不用担心,编译器会产生一个编译错误。Pair<? extends Employee>参数替换后,我们得到如下代码 ? extends Employee getFirst() void setFirst(? extends Employee) 对于get方法,没问题,因为编译器知道可以把返回对象转换为一个Employee类型。但是对于set方法,编译器无法知道具体的类型,所以会拒绝这个调用。

    (2)超类型限定

    超类型限定和子类型限定相反,可以给方法提供参数,但是不能使用返回值。? super Manager这个类型限定为Manager的所有超类。

    Pair<? super Manager>参数替换后,得到如下方法

    ? super Manager getFirst() void setFirst(? super Manager)

    编译器可以用Manager的超类型,例如Employee,Object来调用setFirst方法,但是无法调用getFirst,因为不能把Manager的超类引用转换为Manager引用。

    超类型限定的存在是为了解决下面一类的问题。例如要写一个函数从Manager[]中找出工资最高和最低的两个,放在一个Pair中返回。

    1. public static void minMaxSal(Manager[] mgrs, Pair<? super Manager> pair) {  
    2.     if (mgrs == null || mgrs.length == 0) {  
    3.         return;  
    4.     }  
    5.       
    6.     pair.setFirst(mgrs[0]);  
    7.     pair.setSecond(mgrs[0]);  
    8.     //TODO  
    9. }  
        public static void minMaxSal(Manager[] mgrs, Pair<? super Manager> pair) {
            if (mgrs == null || mgrs.length == 0) {
                return;
            }
            
            pair.setFirst(mgrs[0]);
            pair.setSecond(mgrs[0]);
            //TODO
        }

    如此就可以这样调用

    1. Pair<? super Manager> pair = new Pair<Employee>(null, null);  
    2. minMaxSal(new Manager[] {mgr1, mgr2}, pair);  
            Pair<? super Manager> pair = new Pair<Employee>(null, null);
            minMaxSal(new Manager[] {mgr1, mgr2}, pair);
    

    (3) <T extends Comparable<? super T>>

    超类型限定还有一个使用形式: <T extends Comparable<? super T>>

    比如要写一个函数返回数组中的最小对象。似乎可以如下定义。 public static <T> T min(T[] a) 但是要找到最小值必须进行比较,这样就需要T实现了Comparable接口。为此可以把方法定义改变成 public static <T extends Comparable> T min(T[] a) 因为Comparable接口本身也是泛型的,所以最终可以改写成 public static <T extends Comparable<T>> T min(T[] a)
    但是考虑到如下的使用场景

    1. GregorianCalendar[] birthdays = {  
    2.         new GregorianCalendar(1906, Calendar.DECEMBER, 9),  
    3.         new GregorianCalendar(1815, Calendar.DECEMBER, 10),  
    4.         new GregorianCalendar(1903, Calendar.DECEMBER, 3),  
    5.         new GregorianCalendar(1910, Calendar.JUNE, 22),  
    6. };  
    7.   
    8. System.out.println("Min Age = " + ArrayAlg.min(birthdays).getTime());  
            GregorianCalendar[] birthdays = {
                    new GregorianCalendar(1906, Calendar.DECEMBER, 9),
                    new GregorianCalendar(1815, Calendar.DECEMBER, 10),
                    new GregorianCalendar(1903, Calendar.DECEMBER, 3),
                    new GregorianCalendar(1910, Calendar.JUNE, 22),
            };
            
            System.out.println("Min Age = " + ArrayAlg.min(birthdays).getTime());

    java.util.GregorianCalendar继承自java.util.Calendar。GregorianCalendar本身没有实现Comparable接口,其父类Calendar实现了Comparable接口。 但是编译器会对ArrayAlg.min(birthdays)报错,因为GregorianCalendar实现了Comparable<Calendar> 而不是Comparable<GregorianCalendar>。 只要把min方法的申明改成<T extends Comparable<? super T>>就可以通过编译。 完整的方法如下。 注意,由于一个类可能没有实现Comparable方法,所以还需要定义另外一个方法,传入Comparator对象进行比较。

    1. import java.util.Comparator;  
    2.   
    3. public class ArrayAlg {  
    4.     public static <T extends Comparable<? super T>> T min(T[] a) {  
    5.         if (a == null || a.length == 0) {  
    6.             return null;  
    7.         }  
    8.         T smallest = a[0];  
    9.         for (int i = 1; i < a.length; i++) {  
    10.             if (smallest.compareTo(a[i]) > 0) {  
    11.                 smallest = a[i];  
    12.             }  
    13.         }  
    14.         return smallest;  
    15.     }  
    16.               
    17.     public static <T> T min(T[] a, Comparator<? super T> c) {  
    18.         if (a == null || a.length == 0) {  
    19.             return null;  
    20.         }  
    21.         T smallest = a[0];  
    22.         for (int i = 1; i < a.length; i++) {  
    23.             if (c.compare(smallest, a[i]) > 0) {  
    24.                 smallest = a[i];  
    25.             }  
    26.         }  
    27.         return smallest;  
    28.     }  
    29. }  
    import java.util.Comparator;
    
    public class ArrayAlg {
        public static <T extends Comparable<? super T>> T min(T[] a) {
            if (a == null || a.length == 0) {
                return null;
            }
            T smallest = a[0];
            for (int i = 1; i < a.length; i++) {
                if (smallest.compareTo(a[i]) > 0) {
                    smallest = a[i];
                }
            }
            return smallest;
        }
                
        public static <T> T min(T[] a, Comparator<? super T> c) {
            if (a == null || a.length == 0) {
                return null;
            }
            T smallest = a[0];
            for (int i = 1; i < a.length; i++) {
                if (c.compare(smallest, a[i]) > 0) {
                    smallest = a[i];
                }
            }
            return smallest;
        }
    }

    传入Comparator作为参数的min方法的调用例子。

    1. Manager[] managers = new Manager[] {  
    2.         new Manager("Jack", 10000.99),  
    3.         new Manager("Tom", 10001.01),  
    4. };  
    5.   
    6. Manager minSalMgr = ArrayAlg.min(managers, new Comparator<Employee>() {  
    7.     @Override  
    8.     public int compare(Employee e1, Employee e2) {  
    9.         return Double.compare(e1.getSalary(), e2.getSalary());  
    10.     }              
    11. });  
    12. System.out.println("Min. Salary = " + minSalMgr.getName());  
            Manager[] managers = new Manager[] {
                    new Manager("Jack", 10000.99),
                    new Manager("Tom", 10001.01),
            };
            
            Manager minSalMgr = ArrayAlg.min(managers, new Comparator<Employee>() {
                @Override
                public int compare(Employee e1, Employee e2) {
                    return Double.compare(e1.getSalary(), e2.getSalary());
                }            
            });
            System.out.println("Min. Salary = " + minSalMgr.getName());

    在Java的类库中,大量使用了这种类型的参数。例如在java.util.Collections类中就定义了下面两个方法

    1. public static <T extends Comparable<? super T>> void sort(List<T> list)  
    2. public static <T> void sort(List<T> list, Comparator<? super T> c)   
    public static <T extends Comparable<? super T>> void sort(List<T> list)
    public static <T> void sort(List<T> list, Comparator<? super T> c) 

    (4)无限定通配符?

    无限定通配符表示不需要限定任何类型。例如Pair<?>。 参数替换后的Pair类有如下方法 ? getFirst() void setFirst(?) 所以可以调用getFirst方法,因为编译器可以把返回值转换为Object。 但是不能调用setFirst方法,因为编译器无法确定参数类型,这就是Pair<?>和Pair方法的根本不同。 无限定通配符的出现是为了支持如下的函数定义。

    1. public class PairAlg {      
    2.     public static boolean hasNulls(Pair<?> p) {  
    3.         return p.getFirst() == null || p.getSecond() == null;  
    4.     }  
    5. }  
  • 相关阅读:
    团队项目-选题报告
    第一次结对编程作业
    第一次个人编程作业
    第一次博客作业
    Java web的读取Excel简单Demo
    Java一些常见的出错异常处理
    JSTL截取字符串
    DATAX动态参数数据传递
    DataX实现oracle到oracle之间的数据传递
    DataX安装环境搭建
  • 原文地址:https://www.cnblogs.com/runwulingsheng/p/5147148.html
Copyright © 2020-2023  润新知