泛型
认识泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:) ,称为泛型类、泛型接口,泛型方法、它们统称为泛型。
作用:利用泛型,可以限制集合存储数据的类型.
泛型的本质:把具体的数据类型作为参数传给类型变量。
泛型类
类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
public class ArrayList<E>{
. . .
}
修饰符 class 类名<类型变量,类型变量,…> {
}
- 目标:了解泛型类的定义方式
需求:定义一个MyGenericCls类,要求该类有一个属性,属性的类型可以在创建对象时指定
public class MyGenericCls<E>{
E name;
public void setName(E name) {
this.name = name;
}
public E getName() {
return name;
}
}
//1.创建对象,将name的类型指定为String
MyGenericCls<String> cls1 = new MyGenericCls<>();
cls1.setName("张三");
String name1 = cls1.getName();
System.out.println(name1);
//2.创建对象,将name的类型指定为Integer
MyGenericCls<Integer> cls2 = new MyGenericCls<>();
cls2.setName(666);
Integer name2 = cls2.getName();
System.out.println(name2);
//3.泛型类的作用:可以提高一个类中数据类型的灵活性
泛型接口
- 目标:了解泛型接口的定义方式
public interface A<E>{
. . .
}
修饰符 interface 接口名<类型变量,类型变量,…> {
}
需求:定义一个泛型接口MyGenericInter,并定义实现类实现该接口
public interface MyGenericInter<E> {
//接口名后面定义的泛型(类型变量),在整个接口中要使用数据类型的地方都可以用,可以提高一个接口中类型的灵活性
void method1(E e);
E method2();
}
public class InterImpl1 implements MyGenericInter<String>{
@Override
public void method1(String s) {
}
@Override
public String method2() {
return null;
}
}
public class InterImpl2 implements MyGenericInter<Integer>{
@Override
public void method1(Integer integer) {
}
@Override
public Integer method2() {
return null;
}
}
泛型方法
- 目标:了解泛型方法的定义方式
public static <T> void test(T t){
}
修饰符 <类型变量,类型变量,…> 返回值类型 方法名(形参列表) {
}
需求:定义一个泛型方法,可以接收任意类型的引用数据类型数组,将其变成ArrayList集合返回
/*定义方法:数组的类型不确定!需要提高类型的灵活性
* 明确参数:数组类型,数组的元素使用泛型变量
* 明确返回值:ArrayList集合,集合中存储的元素和数组元素类型保持一致
*/
public static <T> ArrayList<T> arrToList(T[] arr) {
//2.创建ArrayList集合对象
ArrayList<T> list = new ArrayList<T>();
//3.遍历数组
for (int i = 0; i < arr.length; i++) {
T data = arr[i];
list.add(data);
}
return list;
}
Integer[] arr1 = {11,22,33,44,55};
ArrayList<Integer> list1 = arrTolist(arr1);
System.out.println(list1);
String[] arr2 = {"物", "罗"};
ArrayList<Integer> list2 = arrTolist(arr2);
System.out.println(list2);
//[11, 22, 33, 44, 55]
//[物, 罗]
泛型通配符
通配符:?
可以在“使用泛型”的时候代表不确定的类型
泛型的上下限:
泛型上限: ? extends Car
: ? 能接收的必须是Car或者其子类 。
泛型下限: ? super Car
: ? 能接收的必须是Car或者其父类。
4. 目标:了解泛型通配符的使用方式
需求:优化sellApple和sellBanana两个方法,变成一个批量销售所有水果的方法
/**
* 水果类
*/
public class Fruit {
}
//苹果类
public class Apple extends Fruit{
}
//香蕉类
public class Banana extends Fruit{
}
//创建苹果集合
ArrayList<Apple> apples = new ArrayList<>();
//创建香蕉集合
ArrayList<Banana> bananas = new ArrayList<>();
//public static void sellFruit(ArrayList<?> fruits){
//场景:放入集合时,集合中的数据类型不确定,此时使用泛型通配符?,此时?代表一切类型
//public static void sellFruit(ArrayList<?> fruits) {
//场景:此时还是不确定给泛型变量赋什么类型,依然使用通配符?,不过要限制它的范围,使用通配符的上限
//要求:?要么是水果类型,要么是水果的子类类型
public static void sellFruit(ArrayList<? extends Fruit> fruits){
System.out.println("销售水果");
}
//批量销售苹果
public static void sellApple(ArrayList<Apple> apples) {
System.out.println("销售苹果");
}
//批量销售香蕉
public static void sellBanana(ArrayList<Banana> bananas) {
System.out.println("销售香蕉");
}
}
public static void main(String[] args) {
//需求:优化sellApple和sellBanana两个方法,变成一个批量销售所有水果的方法
//创建苹果集合
ArrayList<Apple> apples = new ArrayList<>();
//创建香蕉集合
ArrayList<Banana> bananas = new ArrayList<>();
//销售苹果和香蕉
//sellApple(apples);
//sellBanana(bananas);
//sellBanana(apples);
//sellApple(bananas);
//此时方法的形参类型是ArrayList,所以sellFruit、sellApple、sellBanana三个方法的类型是一样的!
sellFruit(apples);
sellFruit(bananas);
//创建狗集合,并销售
ArrayList<Dog> dogs = new ArrayList<>();
//有些时候,虽然不确定具体是什么类型,但是还是要限制一下类型的范围,此时需要限制类型是水果!
//sellFruit(dogs);
}