原来这就叫泛型

泛型是什么?

泛型即“参数化类型”,将类的具体类型参数化,然后在使用或调用时传入具体的类型。

泛型的用法

//例子:ArrayList
class ArrayList<T>{
     //TODO
}
ArrayList<String> list = new ArrayList<String>();
list.add("你好");//只能加入String
String str = list.get(0);//返回的实例可以直接用String
复制代码

我们在定义一个数组的时候,可以定义这个数组的元素类型的具体类型。但是ArrayList的开发者在开发的时候并不知道用户需要创建什么类型的数组,因此定义了泛型,让用户在使用的时候传入具体的类型,确保add()等操作的时候只能传入该类型的实例,在get()的时候可以直接获取该类型的实例,避免强制cast触发异常。

泛型的进阶用法

使用泛型限制传入类型的范围

class MyArrayList<T extends Fruit>{
    //TODO
}
复制代码

当我们设计一个自定义的ArrayList的时候,我们可以限制传入类型的范围,如<? extends Fruit>表示只能创建一个Fruit或其子类的数组。

泛型的上界<? extends Fruit>

ArrayList<Apple> apples = new ArrayList<Apples>();
ArrayList<? extends Fruit> fruits = apples;
fruits.add(new Apple());//编译器报错
Fruit fruit = fruits.get(0);
复制代码

这<? extends Fruit>在这里表示可以实例Fruit及其子类,但是具体是哪一个类,还不能确定,所以在add操作的时候,我们不能确定需要传入什么类型,所以不能传入任何实例(null除外),在get()操作的时候,我们可以获取到一个Fruit的实例,因为Fruit的子类都可以向上转型为Fruit的实例(苹果、香蕉都是水果)

泛型的下界<? super Apple>

ArrayList<Fruit> fruits = new ArrayList<Fruit>();
ArrayList<? super Apple> apples = fruits;
apples.add(new Fruit());//编译器报错
apples.add(new Apple());
apples.add(new AppleCore())//AppleCore是苹果核
Object object = apples.get(0);
复制代码

这<? super Apple>在这里表示可以实例Apple及其父类,但是具体哪一个类,也不能确定,虽然可以实例Apple的父类,但是不能add Apple的父类实例,因为Apple的父类是不确定的,但是子类是有向上转型,可以直接赋给其父类,所以只能add Apple的子类(苹果可以是水果,也可以是商品,但是水果和商品指代的东西不一定一样),因为只可以add Apple或其子类,所以只能get Apple的实例,因为子类的都有向上转型
ps:提到很多次向上转型,这里说明一下
父类的引用用可以指向子类对象,但是子类的引用不能指向父类对象

Fruit fruit = new Apple();//意思是苹果的实例一定可以指向水果的引用
复制代码

泛型的原理

泛型的作用有二:
1. 帮助检查代码中的类型,提前报错
2. 强制转型
泛型的信息只存在编译阶段,正如泛型的作用所介绍,泛型的存在只是帮助开发者开发过程更便捷,也可以帮助开发者避免一些强转转型导致的错误,而泛型在运行阶段,都会进行泛型擦除,也就是说:

ArrayList<Fruit> fruits = new ArrayList<Fruit>();
ArrayList<Apple> apples = new ArrayList<Apple>();
复制代码

上述两个数组,在调用getClass()发现,二者都是属于ArrayList.class,就是属于同一个对象的不同实例而已

我们定义的泛型去哪里了呢?
比如,ArrayList源码中的ArrayList的去哪里了呢,运行阶段,会将其统一转成Object,也就是说,在ArrayList中的所有数据其实都是Object类型的,只不过我们在编译时,可以指定一种类型,在获取的时候再将Object转成该类型,代码示例:

ArrayList<Fruit> fruits = new ArrayList<Fruit>();//等价于ArrayList fruits = new ArrayList();
Fruit fruit = fruits.get(0);//实际如下
Fruit fruit = (Fruit)fruits.get(0);
复制代码

如代码所示,创建一个Fruit的数组,其实就是创建一个Object的数组,只不过在编译时我们可以通过泛型来让Fruit数组只存Fruit的实例,然后在取数据的时候,从Fruit数组中取到的元素一定是Fruit的实例,只是因为我们存的时候只能存Fruit元素,所以在取的时候就可以不用cast了。

总结:泛型的学习就到这了,泛型的作用就是帮助我们限制类型,自动转型,在运行期并没有泛型,所有、、等都会被编译成Object,说白了,泛型可能就是帮助我们开发者更好的开发,避免一些强转的异常。
(这是一个学习贴,如有纰漏烦请指出,谢谢!)

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享