1、泛型
(1)引入:在没有泛型的时候,当你把一个对象放进java的集合中,集合就会忘记对象的类型,把所有的对象当做Object类型处理,这时可以把所有的类型放进集合。因此取出集合元素后通常还需要进行强制类型转换(调用子类特有功能时),这种强制类型转换不仅增加了编程的复杂度,还可能引发ClassCastException类型转换异常。所以java5引入了“参数化类型”即泛型(Generic)
泛型:可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>
注意:泛型只能支持引用数据类型,如果要操作基本数据类型,则需转换为对应包装类。
扩展知识:java中的泛型是伪泛型。即这个泛型只在编译期间有效,它会在编译期间检查是否为该约束数据类型,而当编译结束,数据添加成功时集合还是会把这些数据当做Object类型来处理,只不过当再次获取数据时,集合的底层会把这些 Object类型的数据按照泛型进行强制转换。该行为叫做泛型的擦除。
细节:
泛型中不能写基本数据类型,只有写对应的包装类才可以.(自定义类属于引用数据类型)指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型.如果不写泛型,默认类型是Object,
泛型定义:
定义在类后面 泛型类
定义在方法上面 泛型方法
定义在接口后面 泛型接口
2、泛型的定义
(1)泛型类
使用场景:当一个类中,某个变量的数据类型不能确定时,就可以定义带有泛型的类
定义泛型类时可以声明多个类型class MyClass<T,E>{ }
格式:
修饰符 class 类名<类型> {
}
举例:public class ArrayList<E> {}创建该类对象时,E就确定类型
此处的E可以理解为变量,但是它不是用来记录数据的,而是记录数据的类型,可以写成T、E、K、V等
定义是不知道存什么类型
下面的代码就是在你创建泛型类对象的时候把你定义的数据类型传递给E。
注:泛型类型个数可以是多个class MyClass<T,E>{ }
import java.util.Arrays; import java.util.Iterator; public class Main { public static void main(String[] args) { /*MyArrayList<String> list = new MyArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); System.out.println(list);*/ MyArrayList<Integer> list = new MyArrayList<>(); list.add(123); list.add(234); list.add(345); int i = list.get(1).intValue(); Integer j = Integer.valueOf(i); System.out.println(list.toString()); } } class MyArrayList<E> { Object[] obj = new Object[10]; int size; //E:表示不确定的类型,该类型在类名后面已经定义过了。 //e:形参的名字,变量名 public boolean add(E e){ obj[size++]=e; return true; } public E get(int index) { return (E)obj[index]; } @Override public String toString() { return Arrays.toString(obj); } }(2)泛型方法
方法中形参类型不能确定时
可以使用类名后面定义的泛型<E> (所有方法都能用)
可以在方法申明上定义自己的泛型 (只有本方法能用)
格式:
修饰符<类型> 返回值类型 方法名(类型 变量名) {
}
举例:当调用add方法的时候,才会确定T类型
public class MyArrayList {
public<T> boolean add(T e) {
obj[size++] = e;
return true;
}
}
import java.util.ArrayList; public class GenericsDemo1 { public static void main(String[] args){ ArrayList<String> list = new ArrayList<>(); ListUtil.addAll(list,"aaa","bbb","ccc"); System.out.println(list); ArrayList<Integer> list1 = new ArrayList<>(); ListUtil.addAll(list1,123,234,345,456,567,678,789); System.out.println(list1); } } class ListUtil { private ListUtil(){} //E...e可变参数,底层是一个数组,可以通过遍历获取每个元素。 public static<E> void addAll(ArrayList<E> list, E...e){ for (E element : e) { list.add(element); } } }(3)泛型接口
当一个接口中类型不确定时,可以用在接口名后面加泛型
格式:修饰符 interface 接口名<类型> {
}
举例:public interface List<E> {}
重点:如何使用一个带泛型的接口
方式一:实现类给出具体类型
方式二:实现类延续泛型,创建对象时在确定
方式一:
import java.util.*; public class GenericsDemo2 { public static void main(String[] args){ MyArrayList2 list = new MyArrayList2();//实现类已经告诉具体类型,所以这里不用再用泛型 list.add("aaa"); list.add("bbb"); list.add("ccc"); System.out.println(list); } } class MyArrayList2 implements List<String> { @Override public boolean add(String stirng) { return false; } }方法二:
import java.util.*; public class GenericsDemo3 { public static void main(String[] args) { //实现类延续接口泛型,不确定实现类是什么类型,此时需传入类型,使用泛型 MyArrayList3<String> list = new MyArrayList3<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); System.out.println(list); } } class MyArrayList3<E> implements List<E> { @Override public boolean add(E e) { return false; } }3、泛型的继承和通配符
(1)泛型的继承
泛型不具备继承性,但是数据具备继承性。所以说
指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型.因为数据具备继承性。
import java.time.Year; import java.util.ArrayList; public class GenericsDemo4 { public static void main(String[] args){ ArrayList<Ye> list1 = new ArrayList<>(); ArrayList<Fu> list2 = new ArrayList<>(); ArrayList<Zi> list3 = new ArrayList<>(); method(list1); //下面调用方法会报错,因为泛型不具备继承性 // method(list2); // method(list3); //下面说明数据具备继承性 list1.add(new Ye()); list1.add(new Fu()); list1.add(new Zi()); } //此时,泛型里面写的是什么类型,那么只能传递什么类型的数据。 public static void method(ArrayList<Ye> list){ } } class Ye { } class Fu extends Ye { } class Zi extends Fu { }(2)泛型的通配符
引入:下面代码中泛型方法有一个小弊端,此时他可以接受任意的数据类型,此时我们想要限定该方法的类型,所以就可以用泛型里面的通配符“?”实现。
通配符:?也表示不确定的类型,它可以进行类型的限定
? extends E :表示可以传递E或者E所有的子类型
? super E:表示可以传递E或者E所有的父类类型
举例:
(1)public static void method(ArrayList<?> list){}
等价于public static<E> void method(ArrayList<E> list){}因为?表示不确定的类型
(2)public static void method(ArrayList<? Extends Ye> list){} 表示可以传递E或者E所有的子类
(3)public static void method(ArrayList<? super Zi> list){} 表示可以传递Zi或者Zi所有的父类
应用场景:
如果我们定义类、方法、接口的时候,如果类型不确定、就可以定义泛型类、泛型方法。泛型接口。
如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型的通配符。
泛型的通配符:
关键点:可以限定类型的范围。
import java.util.ArrayList; public class GenericsDemo5 { public static void main(String[] args) { ArrayList<Yee> list1 = new ArrayList<>(); ArrayList<Fuu> list2 = new ArrayList<>(); ArrayList<Zii> list3 = new ArrayList<>(); ArrayList<Student> list4 = new ArrayList<>(); method(list1); method(list2); method(list3); method(list4); } //泛型方法有一个小弊端,此时他可以接受任意的数据类型。 public static<E> void method(ArrayList<E> list){ } //可以传递Ye和其所有的子类型Fu,Zi. public static void method(ArrayList<? Extends Ye> list){ } //可以传递Zi和其所有的父类型Fu,Ye public static void method(ArrayList<? super Zi> list){ } } class Yee { } class Fuu extends Yee { } class Zii extends Fuu { } class Student { }(3)综合练习
import java.util.ArrayList; public class GenericsDemo6 { public static void main(String[] args) { ArrayList<BossCat> list1 = new ArrayList<>(); ArrayList<LihuaCat> list2 = new ArrayList<>(); ArrayList<TaidiDog> list3 = new ArrayList<>(); ArrayList<HashiDog> list4 = new ArrayList<>(); keepPer(list1); keepPer(list2); keepPer(list3); keepPer(list4); } public static void keepPer(ArrayList<? extends Animal> list){ } public static void keepPer(ArrayList<? extends Cat> list){ } public static void keepPer(ArrayList<? extends Dog> list){ } public static void keepPer(ArrayList<? super BossCat> list){ } public static void keepPer(ArrayList<? super TaidiDog> list){ } } class Animal { private String name; private int age; public Animal(){} public Animal(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void eat() { } } class Cat extends Animal { public Cat(String name, int age) { super(name, age); } } class Dog extends Animal { public Dog(String name, int age) { super(name, age); } } class BossCat extends Cat { public BossCat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("一只叫做" + getName() + "的" + getAge() + "岁的波斯猫,正在吃小饼干"); } } class LihuaCat extends Cat { public LihuaCat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("一只叫做" + getName() + "的" + getAge() + "岁的狸花猫,正在吃鱼"); } } class TaidiDog extends Dog { public TaidiDog(String name, int age) { super(name,age); } @Override public void eat() { System.out.println("一只叫做" + getName() + "的" + getAge() + "岁的泰迪狗,正在吃骨头"); } } class HashiDog extends Dog { public HashiDog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("一只叫做" + getName() + "的" + getAge() + "岁的哈士奇,正在吃肉"); } }