重走安卓进阶路——泛型

ps.原来的标题

  • 为什么我们需要泛型?
  • 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析);
  • 如何限定类型变量?
  • 泛型使用中的约束和局限性;
  • 泛型类型能继承吗?
  • 泛型中通配符类型;
  • 虚拟机是如何实现泛型的?

为什么需要泛型

  1. 泛型扩展了类、接口、方法的适用范围
  2. 提前生命可使用的类型,在编译期提供提醒
  3. 避免List使用时的强制类型转换

如何使用泛型

  1. 泛型类
    在类名后加上例如“”这样的泛型来声明泛型变量,在类体重再添加正式的范型变量/方法后即可使用。

注意:

  • 一般有T、E、K、V等字母可表示泛型,约定俗成
  • 也可以有多个,用逗号间隔,如“
  1. 泛型接口
    声明与泛型类无异, 重点在实现。
    实现有两种方法:
    <1> 用泛型类来实现泛型接口,仍返回泛型
    <2>直接用确定的类型来替换字母泛型,返回的也是指定类型

  2. 泛型方法
    可完全独立,可以不在泛型接口,泛型类中
    例:
    此处的“”是泛型方法的明确标志

public  T genericMethod (T ...a){
  return a[a.length/2];
}

注意:

  • 不是在泛型类里的就是泛型方法
  • 泛型类是使用泛型方法同样要有标志???
  • 在泛型类里的泛型方法完全可以和类中定义的泛型不一样
  • 泛型类上声明的泛型只影响普通方法,泛型类中的泛型方法声明的泛型与泛型类上声明的泛型无关

限定类型变量的应用场景

extends 派生/继承 类或接口
需要在泛型应用过程中要求该泛型拥有某种方法或遵循某种规则

注意:
限定混用类与接口时,类必须写在最前面,且只能有一个(单继承)

约束与局限性

  1. 不能实例化类型变量
    this.data = new T();
  2. 静态域或者方法不能引用类型变量
    因为静态在声明、使用时,泛型很可能还未声明
    除非该静态方法本身就是泛型方法
  3. 如果泛型要使用基本类型,只能用其包装类,因为基本类型不是对象
  4. 范型变量不能使用instance of 方法
  5. 注入不同类型后的泛型类,使用getClass()只能获取其原生类型而不是泛型
    6 Model m = new Model<>();可行
    Model[] ms = new Model[10];不可行
  6. 泛型类不能派生自Throwable,
    如果泛型方法声明T派生自Throwable,不能通过捕获(catch)泛型类对象T,但可通过捕获(catch)Throwable类型,再直接抛出(throws T)

泛型类型的继承规则

  1. 约束与局限性中的第五条可知,存在Class A, Class B extends A
    Model为范行为,Model与Model没有关系,即不允许Model a = new Model();
  2. 要区别于反省类之间的派生
    如 ModelE extends Model
    允许 Model b = new ModelE<>();
    直接例子就是List和ArrayList
  3. 若存在public void set(Model m){}方法
    Model
    a = new Model<>();
    Model b = new Model<>();
    set(a);//可行
    set(b);//报错
    这一条引出了泛型中通配符的使用

通配符

泛型类型的继承规则的第3条中的public void set(Model m){}修改为
public void set(Model m){}即可成立

注意:通配符用于方法或变量,而不能用于泛型类上的泛型声明
限定了上界,意味着能传Model及其子类
限定了下界,意味着能穿Model及父类

因此对进行getData(),只返回Model类型,因为只知上界;
进行setData(),无法传入其子类,因为不知下界;
进行getData()返回Object,因为最上界已限定;
进行setData()可传入其子类,因为已知上界。

虚拟机实现泛型

泛型擦除,jdk会用代码中提到过的最接近的类型来直接替代泛型类型,或插入强制转型的代码。
可以通过方法重载的失效来证明这一点

你可能感兴趣的:(重走安卓进阶路——泛型)