写点什么

01、泛型是什么?,flutterplugin 修改

用户头像
Android架构
关注
发布于: 刚刚

3.1 代码更健壮

泛型将集合的类型检测提前到了编译期,保证错误在编译时就会抛出,基本上代码编辑器(Android Studio、IDEA 等)在书写代码阶段给泛型传入错误类型就会报错。


拥有泛型之前只能在运行时抛出类型转换异常(ClassCastException),代码十分脆弱。


// 泛型存在之前// 集合里存入 Fruit 和 Dog,编译不会报错 List fruits = new ArrayList();fruits.add(new Fruit());fruits.add(new Dog()); // X 错误的插入,直到运行时报错


// 泛型存在之后 List<Fruit> fruits = new ArrayList<Fruit>();fruits.add(new Fruit());fruits.add(new Dog());// X 编译时就会报错

3.2 代码更简洁

泛型省去了类型的强制转换。在没有泛型之前,集合内的对象都会被向上转型为 Object,所以需要强转。


// 没有泛型之前,获取对象需要强转 Fruit fruit = (Fruit) fruits.get(0);

3.3 代码复用性强

泛型就是使用参数化类型,在一段代码上操作多种数据类型。比如:对几个类的处理,在逻辑上完全相同,那自然会想这段逻辑代码只写一遍就好了,所以泛型就产生了。

四、泛型的原理

泛型在 JDK1.5 才出现,为了向下兼容,虚拟机是并不支持泛型的,所以 JAVA 在编译阶段除了进行类型判断,还对泛型进行了擦除,于是所有的泛型在字节码里都变成了原始类型,和 C#的泛型不同,JAVA 使用的是伪泛型

4.1 泛型擦除

在编译阶段生成字节码时,会进行泛型擦除,所以我们看下生成的字节码文件,就可以清晰的看到泛型【T】被转换成了 Object。


// java 代码 public c


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


lass Generics<T> {private T mData;


public T getData() {return mData;}


public void setData(T data) {this.mData = data;}}


下面是 Generics 类生成的字节码


// class version 51.0 (51)// access flags 0x21// signature <T:Ljava/lang/Object;>Ljava/lang/Object;// declaration: com/kproduce/androidstudy/test/Generics<T>public class com/kproduce/androidstudy/test/Generics {


// compiled from: Generics.java


// access flags 0x2// signature TT;// declaration: Tprivate Ljava/lang/Object; mData


// access flags 0x1public <init>()VL0LINENUMBER 6 L0ALOAD 0INVOKESPECIAL java/lang/Object.<init> ()VRETURNL1LOCALVARIABLE this Lcom/kproduce/androidstudy/test/Generics; L0 L1 0// signature Lcom/kproduce/androidstudy/test/Generics<TT;>;// declaration: com.kproduce.androidstudy.test.Generics<T>MAXSTACK = 1MAXLOCALS = 1


// access flags 0x1// signature ()TT;// declaration: T getData()public getData()Ljava/lang/Object;L0LINENUMBER 10 L0ALOAD 0GETFIELD com/kproduce/androidstudy/test/Generics.mData : Ljava/lang/Object;ARETURNL1LOCALVARIABLE this Lcom/kproduce/androidstudy/test/Generics; L0 L1 0// signature Lcom/kproduce/androidstudy/test/Generics<TT;>;// declaration: com.kproduce.androidstudy.test.Generics<T>MAXSTACK = 1MAXLOCALS = 1


// access flags 0x1// signature (TT;)V// declaration: void setData(T)public setData(Ljava/lang/Object;)VL0LINENUMBER 14 L0ALOAD 0ALOAD 1PUTFIELD com/kproduce/androidstudy/test/Generics.mData : Ljava/lang/Object;L1LINENUMBER 15 L1RETURNL2LOCALVARIABLE this Lcom/kproduce/androidstudy/test/Generics; L0 L2 0// signature Lcom/kproduce/androidstudy/test/Generics<TT;>;// declaration: com.kproduce.androidstudy.test.Generics<T>LOCALVARIABLE data Ljava/lang/Object; L0 L2 1// signature TT;// declaration: TMAXSTACK = 2MAXLOCALS = 2}


看完上面的代码有的同学就喊了,这不备注里面还是有泛型【T】吗?



是的,你们说的没错!那为什么泛型还在备注里面?这时候要说到反射这个概念。


反射是在运行时对于任何一个类,都可以知道里面所有属性和方法。对于任何一个对象,都可以调用它的方法和属性。是 JAVA 被视为动态语言的关键。


既然反射要知道所有的方法和属性,但是泛型在字节码里面被进行了擦除,那 JAVA 就使用备注的方式将泛型偷偷的写入到了字节码里面,保证反射的正常使用。

4.2 泛型擦除原则

  • 如果泛型没有限定(),则用 Object 作为原始类型。

  • 如果有限定(),则用 A 作为原始类型。

  • 如果有多个限定(<T extends A&B>),则使用第一个边界 A 作为原始类型。

五、泛型的限定通配符

通配符是让泛型的转型更灵活


  • <? extends A> 是指“上界通配符”

  • <? super A> 是指“下界通配符”

5.1 通配符存在的意义

数组是可以向上转型的:


Object[] nums = new Integer[2];nums[0] = 1;nums[1] = "string"; // nums 在运行时是一个 Interger 数组,所以会报错


再看一段会报错的泛型转型代码:


// Apple extends Fruit,但是这样转型会报错 List<Fruit> fruits = new List<Apple>();


由此可知,泛型的转型和泛型类型是否继承(Apple extends Fruit)没有任何关系,泛型无法像数组一样直接向上转型,所以通配符的意义就是让泛型的转型更灵活

5.2 通配符详解

  • 上界通配符:<? extends Fruit>,Fruit 是最上边界,只能 get,不能 add。(详解在代码备注中)



public static void main(String[] args) {List<GreenApple> greenApples = new ArrayList<>();List<Apple> apples = new ArrayList<>();List<Food> foods = new ArrayList<>();setData(greenApples);setData(apples);setData(foods); // 编译错误,不在限制范围内}


public void setData(List<? extends Fruit> list){// 上界通配符,只能 get,不能 add// 【只能 get】因为可以确保 list 被指定的对象一定可以向上转型成 Fruit// 【不能 add】因为设置的话无法确定是哪个子类,// 有可能会将 Banana 设置到 List<Apple>里面,所以不能 setFruit fruit = list.get(0);}

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
01、泛型是什么?,flutterplugin修改