Java中的泛型

456 阅读4分钟
  • 为什么我们需要泛型?
  • 泛型类的定义
  • 限定类型变量
  • 泛型的局限性
  • 泛型类型的继承规则
  • 通配符
  • 虚拟机是如何实现泛型的

1. 为什么我们需要泛型?

  • 适用于多种数据类型执行相同的代码
    不用泛型,用重载的话,太过繁琐,代码都一样的,但是多出一种类型就得定义一个方法。

image.png

  • 泛型中的类型在使用时指定,不需要强制类型转换

image.png

2. 泛型类的定义

泛型,即“参数化类型”。 所以泛型就是一个参数的类型。(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。即泛型在使用过程中,T会自动替换成使用的那个对象的类型。

泛型可以用在类、接口、方法中,常用字母T,E,K,V。并且用<>括起来,并放在类名的后面。如果有多个,用“,”隔开来。泛型接口跟类一样

image.png

实现泛型接口有两种方法:
1.在类名那 implements(实现)时用的T类型,得在new的时候指定其具体类型

image.png

image.png

2.在类名那 implements(实现)时用的具体类型,得在new的时候不用指定其具体类型

image.png

泛型方法:

一定要在返回值前面加 < T> 要不然就不是泛型方法,而是普通方法的一个参数是泛型的
image.png

image.png

3. 限定类型变量

泛型可以啥都传,有的时候我们需要限制传入的类型,比如只能传 水果,或者得实现Comparable接口等,就需要用到限定类型变量:
extends,继承,规范的说是扩展。(T extends Fruits&Comparable)
(T extends Fruits&Comparable)在泛型里 extends 可以是类,也可以是接口,也就是省略了implements这个关键词,但是只能继承一个类,可以实现多个接口。类得放第一个,接口排后面,用“&”一个一个排下去

image.png
这样就只有实现了Comparable接口才能调用上面的方法。

4. 泛型的局限性

  1. 只能用对象,不能用基本类型。(例:List< int> a)
  2. 类型擦除!!!(实际运行时虚拟机会将泛型的类型都擦除,转换成Object,也就是List< String>和List< Integer> 这两种其实是一个类型。用getClass()或getClass().getname()取出来是一样的.都是指向List那个类而不是具体的String Integer)

image.png
3. 在静态中,泛型类的泛型变量失效
原因:执行类的顺序,先执行静态,在执行构造函数,所以有静态方法的时候,类还没有呢,所以类上的泛型变量当然没有用。
image.png
4. 不能new(实例化) - data=new T()
5. 不能捕获泛型类的实例

image.png

5. 泛型类型的继承规则

List< Fruit> 和 List< Apple> 没有继承关系,毛关系没有。List< Fruit>和List< Apple>都是Listl类型的(getClass()) ,他们的参数是继承关系,但是他们没有关系。

泛型可以继承泛型,比如List和ArrayList

image.png

6. 通配符

见5:因为List< Fruit> 和 List< Apple> 没有继承关系,但是我们希望用泛型的时候,参数有继承关系的,他们也有继承关系,就引入的通配符 “?”

Orange继承Fruit,但是GenericType< Fruit>却跟GenericType< Orange>没有继承关系,所以不能调用。 GenericType<>是一个整体的类型
image.png

image.png

通配符 “?” (只能用在方法上!) 用了通配符以后,里面参数继承外面整体就继承,就能调用上面方法了
有两种:? extends X,? super X

? extends X (入参?是x的子类,包括X)

主要用于安全地访问数据,可以访问X及其子类型,并且不能写入非null的数据。(可以get取数据,不能set存数据)
因为?是Fruit的子类,所以get取出来最少是Fruit(Fruit y=new Apple()可以)(Apple y=new Fruit()不行)
set方法只知道传入的是个Fruit,至于具体是Fruit的那个子类
image.png

image.png

? super X (入参?是x的父类,包括X)

主要用于安全地写入数据,可以写入X及其子类型
?是Fruit的父类,但是不知道是哪一个,所以get的时候,只能取出最顶层的Object。
set的时候Fruit的子类都能存,因为Fruit的子类都能安全的转成Fruit。但是Fruit的父类却不知道具体是哪个,就不能转了。
image.png

7. 虚拟机是如何实现泛型的

泛型是java后期引进的,为了适配早期的java,所以java中的泛型都是伪泛型,也就是编译运行成Class文件的时候,编译器会将类型擦除。泛型类型都会变回原生类型。最后再相应的地方强转回原来的类型。

Java语言中的泛型实现方法称为类型擦除
例:ArrayList<int>与ArrayList<String>就是同一个类