zoukankan      html  css  js  c++  java
  • Core Java笔记 8.泛型(II)

    本章重点:

    • 高级语法:通配符
    • 最佳实践

    本文介绍泛型的高级语法已经最佳实践。Java 泛型的很多限制都可以从上篇中的原理(Java 泛型机制) 来解释。

    通配符

    通配符是使用的角度而言的,在编译器期间执行更加严格的检查。

    子类限定通配符

    语法:
    Pair<? extends Employee>

    继承关系:
    corejava-generic-pic01
    限制:
    只需要从编译器如何翻译代码块的角度思考这些限制。

    Employee emp = new Employee();
    Manager ceo = new Manager();
    Manager cfo = new Manager();
    
    Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
    Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
    
    // 编译器只知道是某个Employee的子类,编译器推断不出具体的类型
    // 限制: 编译器拒绝任何特定类型
    // void setFirst(? extends Employee)
    wildcardBuddies.setFirst(ceo); // compile-time error
    wildcardBuddies.setFirst(emp); // compile-time error
    wildcardBuddies.setFirst(new Object()); // compile-time error
    
    // ? extends Employee getFirst()
    Employee ret = wildcardBuddies.getFirst(); // OK
    

    超类限定通配符

    语法:
    Pair<? super Manager>

    继承关系:
    corejava-generic-pic02

    限制:

    Employee e1 = new Employee();
    Employee e2 = new Employee();
    Manager ceo = new Manager();
    Pair<Employee> employeeBuddies = new Pair<Employee>();
    Pair<? super Manager> wildcardBuddies = employeeBuddies;  // OK
    
    // 编译器值知道是某个Manager的父类
    // 限制: 编译器拒绝任何特定类型
    // ? super Manager
    Manager ret = wildcardBuddies.getFirst(); // compile-time error
    
    // void setFirst(? super Manager)
    wildcardBuddies.setFirst(e1);  // compile-time error
    wildcardBuddies.setFirst(ceo); // OK
    

    更加严格的写法:

    public static <T extends Comparable> T min(T[] a);
    
    // 更加严格的写法
    public static <T extends Comparable<? super T>> T min(T[] a);
    

    解释:
    该泛型方法使用Comparable<? super T>进行擦除,现在 compareTo 写成:
    int compareTo(? super T). 表明只需要 T的父类有 compareTo() 方法即可.
    例如:GregorianCalendar 是 Calendar 的子类,Calendar实现了Comparable, 但是 GregorianCalendar 并没有重写Comparable, 所以:
    public static <T extends Comparable> T min(T[] a); 是不够的。

    补充:无限定通配符

    语法:
    Pair<?>

    限制:

    ? getFirst();  // 只能赋给一个Object
    void setFirst(?); // 无法调用, Object也不行
    

    存在的理由:
    对于简单的操作,代替泛型方法,更加具有可读性。
    比如:

    public static <T> boolean hasNulls(Pair<T> p)
    // 可以写成:
    public static boolean hasNull(Pair<?> p) {
       return p.getFirst() == null || p.getSecond() == null; 
    }
    

    局限:
    ? 不能作为一种类型。? t = xx 非法。
    这样导致只能代替操作简单的泛型方法。比如swap都写不了.

    public static void swap(Pair<?> p) {
        ? t = p.getFirst;   // ERROR
        p.setFirst(p.getSecond);
        p.setSecond(t);
    }
    
    // 这时候只能:
    public static <T> void swapHelper(Pair<T> p) {
        T t = p.getFirst;   // ERROR
        p.setFirst(p.getSecond);
        p.setSecond(t);
    }
    
    public static void swap(Pair<?> p) {
        swapHelper(p);
    }
    

    最佳实践

    1.不能使用基本类型实例化类型参数. 才有 wrapper 即可.

    2.类型查询只适用于原始类型. 即类型检查直接使用原始类型即可.

    if (a instanceof Pair<String>) // same as "a instanceof Pair"
    if (a instanceof Pair<T>) // T is ignored
    Pair<String> p = (Pair<String>)a; // can only test that a is a Pair
    
    Pair<String> stringPair = ...;
    Pair<Employee> employeePair = ...;
    if (stringPair.getClass() == employeePair.getClass()) // they are equal, Pair.class
    

    3.泛型类无法扩展 Throwable,也无法抛出泛型类.

    // 1. 泛型类无法扩展 Throwable
    public class Problem<T> extends Exception {/*...*/} // ERROR -- can't extend Throwable(编译无法通过)
    // 2. 不能抛出泛型类示例
    public static <T extends Throwable> void doWork(Class<T> t) {
        try {
            do work
        } catch (T e) {// ERROR -- cant't catch type variable
            Logger.global.info(...)
        }
    }
    // 正确时间:
    public static <T extends Throwable> void doWork(Class<T> t) {
        try {
            do work
        } catch (Throwable realCause) {
            t.intCause(realCause);
            throw t;
        }
    }
    

    4.参数化类型的数组不合法,而是采用容器类存储.

    Pair<String>[] table = new Pair<String>[10];  // ERROR
    

    5.可以声明类型参数,但不能实例化类型参数

    new T(...), new T[...], T.class 非法. 但是可以声明.

    问题1.

    public Pair() { first = new T(); second = new T(); } // ERROR
    first = T.class.newInstance(); // ERROR
    
    // 最佳实践: 指定T的类型
    public static <T> Pair<T> makePair(Class<T> clazz) {
        try {
            return new Pair(clazz.newInstance(), clazz.newInstance());
        } catch (Exception e) {
            return null;
        }
    }
    Pair<String> stringPair = Pair.<String>makePair(String.class);// OK
    

    问题2.

    public static <T extends Comparable<? super T>> T[] minmax(T... a) {
        // T[] mm = new T[2]; // ERROR, Type parameter 'T' cannot be instantiated directly
        Object[] mm = new Object[2];
        mm[0] = a[0];
        mm[1] = a[1];
        return (T[])mm; // T 被擦成Comparable, 运行时会ClassCastException, 而Object不是Comparable
    }
    
    public static void main(String[] args) {
        String[] ss = minmax("Tom", "Dick", "Harry"); // ClassCastException!!!
    }
    // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable
    }
    
    // 最佳实践: 
    public static <T extends Comparable<? super T>> T[] minmax(T... a) {
        T[] mm = (T[])Array.newInstance(a.getClass().getComponentType(), 2);
        ...
        return mm;
    }
    

    问题3.

    public class ArrayList<T> {
        private T[] elements;
    
        public ArrayList() { elements  = (T[]) new Object[10]; }
    
        public T get(int n) { return elements[n]; }
        public void set(int n, T e) { elements[n] = e; }
    
        public static void main(String[] args) {
            ArrayList<String> src = new ArrayList<String>();
            src.set(0, "hello");
            src.set(1, "world");
            // String[]和Object[]是不同的类型.
    //        String[] copy = src.toArray(); // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
            
            String[] copy2 = new String[100];
            src.toArray(copy2);
            System.out.println(Arrays.toString(copy2));  // OK
        }
        
        public T[] toArray() {
            // 可以分析:elements.getClass().getComponentType()实质是Object, 所以返回的类型实质是:Object[],
            T[] result = (T[]) Array.newInstance(elements.getClass().getComponentType(), elements.length);
            return result;
        }
    
        public T[] toArray(T[] src) {
            for (int i = 0; i < elements.length; ++i) {
                src[i] = elements[i];
            }
            return src;
        }
    }
    

    6.不能在静态域或者方法中使用类型变量. 因为静态域是类共享的.

    7. 当T和S有继承关系时,Pair<T>Pair<S> 没有继承关系.

    8. Java SE5.0 增加了对泛型的反射API.

    Java SE5.0 增加了 java.lang.reflect.Type 来支持类型的反射信息.
    public void <T extends Comparalbe<? super T>> minmax(T... a);

    corejava-generic-pic03

  • 相关阅读:
    2.5.1 选择器中含有特殊符号的注意事项
    动手为王:由一条UPDATE语句引发的一波三折深入实践(含PPT)
    基于JSR-356实现的Tyrus WebSocket框架的消息传递机制初步了解
    基于JSR-356实现的Tyrus WebSocket框架的消息传递机制初步了解
    Pre-shared key
    Pre-shared key
    Pre-shared key
    Pre-shared key
    confluence+jira
    confluence+jira
  • 原文地址:https://www.cnblogs.com/nil2inf/p/4491376.html
Copyright © 2011-2022 走看看