泛型
泛型
泛型是JDK5引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型格式:<数据类型>
注意:泛型只能用引用数据类型
如果不加泛型,那么在添加数据的时候会默认为是Object类,但是多态的弊端是不能访问子类的特有功能,而且进行强转的话也会出问题,如果有一个是Integer,你要强转成String类型,肯定会报错。
泛型的好处:
- 统一数据类型
- 把运行期间的问题提前到了编译期间,避免强制类型转换可能出现的异常,因为在编译阶段类型就确定下来了。
扩展知识:泛型在Java中是伪泛型,只在编译阶段有效。编译为class字节码文件的时候,泛型就消失,这种现象叫做泛型的擦除。
泛型的细节:
- 泛型中不能写基本数据类型。
- 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型。
- 如果不写泛型,类型默认是Object
泛型可以在很多地方进行定义。
使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。
泛型类
自己重写ArrayList
public class MyArrayList<E> {
Object[] obj = new Object[10];
int size;
public boolean add(E e){
obj[size] = e;
size++;
return true;
}
public E getIndex(int index) {
return (E) obj[index];
}
@Override
public String toString() {
return Arrays.toString(obj);
}
}
泛型方法
方法中形参类型不确定时
-
可以使用类名后面定义的泛型
-
在方法上申明定义自己的泛型
public class MyArrayList{ public <E> boolean add(E e){ obj[size] = e; size++; return true; } }
案例:
定义一个工具类ListUtil,类中定义一个静态方法addAll,用来添加多个集合的元素
public class ListUtil {
private ListUtil(){
}
/**
* 参数一:集合
* 参数二 ~ 最后 要添加的数据
*/
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);
}
}
}
泛型接口
public interface 接口名<E>{
}
重点:如何使用泛型接口?
-
实现类给出具体类型
public class MyArrayList2 implements List<String>{ }
-
实现类延续泛型,创建对象时再确定。
public class MyArrayList2 implements List<E>{ } //创建该对象 MyArrayList2<String> list = new MyArrayList2<>();
泛型的继承和通配符
-
泛型不具备继承性,但是数据具有继承性。
定义几个类,父类 Ye 子类Fu 子类Zi
public static void method(ArrayList<Ye> list){
}
main方法{
ArrayList<Ye> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
method(list1);
method(list2); //报错
method(list3); //报错 method方法指定泛型类型为Ye,泛型不能继承,所以子类使用会报错
}
泛型方法里面写什么类型,传递的就只能是什么类型的数据,
弊端:
它可以接收任意数据类型
我希望:本方法虽然不确定类型,但是我只希望只能传递Ye Fu Zi
此时我们就可以使用泛型的通配符:
?也表示不确定的类型
但是它可以进行类型限定
? extends E :表示可以传递E或者E所有的子类类型
? super E :表示可以传递E 或者 E 所有的父类类型。
应用场景:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
泛型的通配符:
关键点:可以限定类型的范围。
综合练习
需求:
定义一个继承结构:
动物
丨 丨
猫 狗
丨 丨 丨 丨
波斯猫 狸花猫 泰迪 哈士奇
属性:名字、年龄
行为:吃东西
方法体打印:一只叫XXX的,X岁的波斯猫,正在吃小饼干
方法体打印:一只叫XXX的,X岁的狸花猫,正在吃鱼
方法体打印:一只叫XXX的,X岁的泰迪,正在吃骨头,边吃边蹭
方法体打印:一只叫XXX的,X岁的哈士奇,正在吃骨头,边吃边拆家
测试类中定义一个方法,用于饲养动物:
public static void keepPet(ArrayList<???> list){
//遍历集合,调用动物的eat方法
}
要求1:该方法能养所有品种的猫,但不能养狗。
要求2:该方法能养所有品种的狗,但不能养猫。
要求3:该方法能养所有品种的猫,但不能养狗。
在测试类前,根据继承结构创建各个对象,各结点类型为抽象类,叶子结点实现自己的父类并且进行方法重写即可
public class Test {
public static void main(String[] args) {
ArrayList<PersianCat> list1 = new ArrayList<>();
ArrayList<LiHuaCat> list2 = new ArrayList<>();
ArrayList<TeddyDog> list3 = new ArrayList<>();
ArrayList<HuskyDog> list4 = new ArrayList<>();
list1.add(new PersianCat("aaa",3));
list2.add(new LiHuaCat("bbb",3));
keepCat(list1);
keepCat(list2);
list3.add(new TeddyDog("ccc",3));
list4.add(new HuskyDog("ddd",3));
keepDog(list3);
keepDog(list4);
}
public static void keepCat(ArrayList<? extends Cat> list){
for (int i = 0; i < list.size(); i++) {
Cat cat = list.get(i);
cat.eat();
}
}
public static void keepDog(ArrayList<? extends Dog> list){
for (int i = 0; i < list.size(); i++) {
Dog dog = list.get(i);
dog.eat();
}
}
public static void keepAnimal(ArrayList<? extends Animal> list){
for (int i = 0; i < list.size(); i++) {
Animal animal = list.get(i);
animal.eat();
}
}
}