• Java笔记06


    泛型是一种"代码模板"

    什么是泛型

    • T可以是任何class. 编写一次模板, 创建任意类型的ArrayList
    • 泛型是定义一种模板, 例如: ArrayList<T>, 然后再代码中为用到的类创建ArrayList<类型>
    • 编写一次, 万能匹配, 通过了编译器又保证了类型安全

    向上转型

    • 通过应用接口, 实现向上转型
    • ArrayList<T> implements List<T>: ArrayList<T>向上转型为List<T>
    • 模板之间没有任何继承关系: ArrayList<Integer>ArrayList<Number>两者完全没有继承关系
    • 在向上继承时, T不可变!

    使用泛型

    • 使用时, 如果不定义泛型类型, 默认是Object.
    • 如果泛型定义为Object没有发挥泛型的优势.
    • 编译器自动推断, 可以省略List<Number> list = new ArrayList<>;

    泛型接口

    • 可以在接口中定义泛型类型, 实现此类接口必须实现正确的泛型类型

    编写泛型类

    • 一般来说, 泛型类用在集合类中, 我们很少编写泛型类
    • 就是这个类, 可以针对某个特定的类型, 成为一个专门的类
    • 在类后面定义一个<T>, 然后在这个类里面就有了这么一种类型T, 任意使用
    • 泛型类型<T>不能用于静态方法
    • 静态方法, 可以单独改写为"泛型"方法.
    • 静态方法的泛型, 和实例类型的泛型不是一个, 应该清楚的区分开
    public class Pair<T> {
      private T first;
      private T last;
      public Pair(T first, T last) {
        this.first = first;
        this.last = last;
      }
      public T getFirst() {
        return first;
      }
      public T getLast() {
        return last;
      }
      
      // 对于静态方法使用<T>
      public static<K> Pair<K> create(K first, K last) {
        return new Pair<K>(first, last);
      }
    }
    

    多个反省类型

    • 泛型可以定义多种类型.<T, K>
    • 使用的使用, 分别指出两种类型即可Pair<String, Integer>

    擦拭法

    • 泛型是一种模板技术, 不同语言实现方式不同, java是擦拭法
    • 擦拭法: 虚拟机对泛型其实一无所知, 所有的工作都是编译器做的
    • Java的泛型是由编译器在编译时实行的, 编译器内部永远吧所有类型T视为Object处理.
    • 在需要转型的时候, 编译器会根据T的类型, 自动为我们实行安全地强制类型.

    java发型的局限

    • <T>不能是基本类型, 例如int, 因为实际类型是Object.
    • 无法取得带泛型的Class: <T>Object, 无论T的类型是什么, getClass()返回同一个Class实例
    • 无法判断带泛型的Class: 不存在Pair<String>.class, 只存在唯一的Pair.class
    • 不能实例化T类型: 只能通过反射传入实现.

    不恰当的覆写方法

    • 定义的equals(T t)不会覆写成功, 因为摩擦成equals(Object t), 这就是一个覆写方法了

    泛型继承

    class IntPair extends Pair<Integer> {
      public IntPair(Integer first, Integer last) {
        super(first, last);
      }
    }
    public class Pair<T> {
      private T first;
      private T last;
      public Pair(T first, T last) {
        this.first = first;
        this.last = last;
      }
    }
    // 可以直接使用: Integer ip = new IntPair(1, 2)
    
    // 获取继承泛型类型方法
        Class<IntPair> clazz = IntPair.class;
        Type t = clazz.getGenericSuperclass();
        if (t instanceof ParameterizedType) {
          ParameterizedType pt = (ParameterizedType) t;
          Type[] types = pt.getActualTypeArguments();
          Type firstType = types[0];
          Class<?> typeClass = (Class<?>) firstType;
          System.out.println(typeClass);
        }
    
    • 因为java中引入了泛型, 所以只用Class来标识泛型不够用. 所以: java类型的体系如下:
      ┌────┐
      │Type│
      └────┘


      ┌────────────┬────────┴─────────┬───────────────┐
      │ │ │ │
      ┌─────┐┌─────────────────┐┌────────────────┐┌────────────┐
      │Class││ParameterizedType││GenericArrayType││WildcardType│
      └─────┘└─────────────────┘└────────────────┘└────────────┘

    extends通配符

    • Pair<Integer>类型, 符合参数Pair<? extends Number>: 上界通配符, 类型T上界限定在Number
    • 方法参数签名setFirst(? extends Number)无法传递任何Number类型给setFirst(? extends Number)
    • 唯一的例外可以传入null, // ok, 但是后面排除NullPointerException

    extends通配符的作用

    • 可以通过get获取一个指定类型的返回结果
    • 无法通过set进行设置和修改类型
    • 使用extends通配符表示可以读, 不能写.

    使用extends限定T类型

    • 在定义泛型类型Pair<T>的时候, 可以使用extends通配符来限定T的类型

    super通配符

    • 允许set(? super Integer)传入Integer的引用
    • 不允许调用get()方法获得Integer的引用
    • 使用<? super Integer>通配符作为方法参数, 便是方法内部代码对于参数只能写, 不能读

    对比extendssuper通配符

    • <? extends T>允许调用方法T get()获取T的引用, 但不允许调用方法set(T)传入T的引用(传入null除外)
    • <? super T>允许调用方法set(T)传入T的引用, 但不允许调用方法T get()获取T的引用(获取Object除外)
    class Collections {
      public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        //  编辑器可以避免: 意外读取dest, 意外操作src. 安全操作数组
        for (int i = 0; i < src.size(); i++) {
          T t = src.get(i);
          dest.add(t);
        }
      }
    }
    

    PECS原则

    • Producer Extends Consumer Super
    • 如果需要返回T, 它是生产者(Producer), 需要使用extends通配符
    • 如果需要写入T, 它是消费者(Consumer), 需要使用super通配符
    • 需要返回Tsrc是生产者, 声明为<? extends T>
    • 需要写入Tdest是消费者, 声明为<? super T>

    无限定通配符

    • 无限定通配符(Unbounded Wildcard Type), 即只定义一个?
    • void sample(Pair<?> p) {}
    • <?>具体作用:
      • 不允许调用set<T>方法传入引用T(null除外)
      • 不允许调用T get<>方法并获取T引用(只能获取Object引用)
      • 换句话说: 既不能读, 也不能写, 只能做一些null判断
    • 只做一些null判断, 大多数情况下, 引入泛型参数<T>消除<?>通配符
    • Pair<?>是所有的Pair<T>的超类
    Pair<Integer> p = new Pair<>(123, 456);
    Pair<?> p2 = p; // 安全的向上转型
    

    泛型和反射

    • Class<T>就是泛型
    • 调用ClassgetSuperclass()方法返回的Class类型是Class<? super T>
    • 构造方法Constructor<T>也是泛型
    Class<Integer> clazz = Integer.class;
    Constructor<Integer> cons = clazz.getConstructor(int.class);
    Integer i = cons.newInstance(123);
    
    • 可以声明但泛型的数组, 但不能用new操作符创建, 必须通过强制转型实现.
    // Pair<String>[] ps = null; // ok
    // Pair<String>[] ps = new Pair<String>[2]; // Cannot create a generic array of Pair<String>
      Pair<String>[] ps = (Pair<String> []) new Pair[2];
    
    • 使用泛型数组, 是有风险的, 必须扔到初始数组的引用, 直接进行强制转换
    class ABC<T> {
    //  T[] createArray() {
    //    return new T[5];
    //  }
      T[] createArray(Class<T> cls) { // 借助`Class<T>创建泛型数组`
        return (T[]) Array.newInstance(cls, 5);
      }
    }
    

    谨慎使用泛型可变参数

    疑问

    • intInteger两种类型的关系和区别?
    • 谨慎使用泛型可变参数中, 为什么第二个会报错的原因没看懂.
  • 相关阅读:
    真正的e时代
    在线手册
    UVA 10616 Divisible Group Sums
    UVA 10721 Bar Codes
    UVA 10205 Stack 'em Up
    UVA 10247 Complete Tree Labeling
    UVA 10081 Tight Words
    UVA 11125 Arrange Some Marbles
    UVA 10128 Queue
    UVA 10912 Simple Minded Hashing
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/12655285.html
Copyright © 2020-2023  润新知