泛型
基本原理
作用: 可以在编译阶段约束操作的数据类型,并进行检查。
格式: <数据类型>
注意: 泛型只能支持引用数据类型。
没有泛型时,集合如何存储数据
若没有泛型时,我们可以给集合添加任意的数据类型,基本数据类型、引用数据类型、甚至是对象。
//1.创建集合对象
ArrayList list = new ArrayList();
//2.添加数据
list.add(123);
list.add("aaa");
list.add(new Student());
此时由于没有写泛型,遍历时所有的数据都会被提升为Object类型。
//3.遍历集合获取里面的每一个元素
Iterator it = list.iterator();
while(it.hasNext()){
Object obj = it.next();
}
//多态的弊端出现了,不能访问子类的特有功能,也就是说此时不能去使用集合中的一些方法
//若是使用强转,其他的数据类型就无法正常使用了
为了解决这个问题,泛型出现在了现在的Java语言中。
好处
泛型的好处:
- 统一数据类型
- 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。
扩展
Java中的泛型是伪泛型。
在Java文件中,我们在集合中运用了泛型,但是等到了class文件时,泛型就会消失,这也被成为泛型的擦除。
若是在Java文件中,我们添加进集合的数据不为字符串,则会直接报错异常。
细节
泛型的细节:
- 泛型中不能写基本数据类型
- 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
- 如果不写泛型,类型默认为Object
泛型的使用
泛型类
使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
格式:
修饰符 class 类名<类型>{
}
实例:
public class ArrayList<E>{
}
此时的E可以理解为变量,但是不是用来记录数据的,而是记录数据类型的。
我们可以用这个方式去定义一个泛型类。
public class MyArrayList<E>{
Object[] obj = new Object[10];
int size;
//E:表示是不确定的类型,该类型在类名后面已经定义过了
//e:形参的名字,变量名
public boolean add(E e){
obj[size] = e;
size++;
return true;
}
}
这时我们就定义了一个add方法,但是如果要使用这个方法,我们不能再用E了,而是用一个确切的数据类型来使用它,例如String。
泛型方法
使用场景:方法中形参类型不确定时,可以使用类名后面定义的泛型;
- 方案一:使用类名后面定义的泛型(所有方法都能使用)
- 方案二:在方法申明上定义自己的泛型(只有本方法能用)
格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名){
}
实例:
public <T> void show(T t){
}
此处T可以理解为变量,记录类型的。
public class ListUtil{
private ListUtil(){}
//类中定义一个静态方法addAll,用来添加多个集合的元素
//参数一:集合
//参数二:要添加的元素
//这里的括号中,不仅要定义list的数据类型,还要定义元素的数据类型,数量就是集合的长度
public static<E> void addAll(ArrayList<E> list,E e1,E e2,E e3,E e4){
list.add(e1);
list.add(e2);
list.add(e3);
list.add(e4);
}
//这里还有一种扩展方法,当我们不知道要准备多少数量时可以使用,可以一直添加元素
public static<E> void addAll(ArrayList<E> list,E...e){
for(E element : e){
list.add(element);
}
}
泛型接口
使用场景:接口中数据类型不确定时,可以使用泛型接口
格式:
修饰符 interface 接口名<类型>{
}
实例:
public interface List<E>{
}
- 方式一:实现类给出具体类型
public class MyArrayList implements List<String>{
}
此时List的泛型是确定的,所以继承这个类型。
- 方法二:实现类延续泛型,创建对象时再确定
public class MyArratList<E> implements List<E>{
}
这时实现类的泛型也是不确定的,我们可以直到调用时再去确定它的数据类型。
泛型的继承和通配符
- 泛型不具备继承性,但是数据具备继承性
也就是说,泛型里面写的是什么类型,那么只能传递什么类型的数据,不存在继承关系。
但有时候我们可能想去规定一个范围,例如一些数据类型可以传递,一些不可以传递,该怎么办呢?
这时候,我们可以使用泛型的通配符:?
?也表示不确定的类型,它可以进行类型的限定。
例如:
- ? extends E: 表示可以传递E或者E所有的子类类型
public static void method(ArrayList<? extends Person> list){
}
- super E: 表示可以传递E或者E所有的父类类型
public static void method(ArrayList<? super Student> list){
}
应用场景:
- 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
- 如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型的通配符。
关键点: 可以限定类型的范围。