概述:
泛型是代表未来的任意引用数据类型,不确定的数据类型,没法书写具体类型名称,所以Java中使用字母来暂时的代表未来的数据类型格式:<字母> <字母1,字母2> <字母1字母2,字母3>
注意:
字母可以是任意的字母【一般使用大写字母 常见 E T K V】
一个泛型的字母的个数可以任意的 【一般使用1个字母】
一个<>可以写多个泛型 泛型和泛型之间使用 逗号 隔开
使用:
使用<大写字母>格式来声明泛型,
泛型只有声明了之后才可以使用,
泛型使用后真正使用的时候需要具体化【实例化】
好处:
1、提供代码的安全性,把运行时的异常提前到编译时发生。
2、避免了强制的类型转换
代码示例:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericDemo {
public static void main(String[] args) {
//创建集合
List<String> list = new ArrayList<String>();
//list.add(100); //未定义泛型的时候可以添加,定义后编译报错
list.add("java");
//创建迭代器对象
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
// Object next = iterator.next(); //此时返回的是Object类型
// String s = (String)next; //没有泛型实例化需要强制类型转换
// System.out.println(s.startsWith("j") ); //true
System.out.println(iterator.next().startsWith("j")); //true
}
}
}
泛型类
概述:
具有泛型的类就叫做泛型类
类提前定义了一个未来的数据类型,类中的变量就可以使用这个泛型
格式:
修饰符 class 类名 < E > { 类的内容 }
泛型类的泛型实例化时机:
创建类对象的时候需要制定具体的数据类型
练习
定义一个泛型类,只能在这个类的头部进行增删操作
分析:
1、定义一个类 需要在类名后面声明泛型【不知道】
2、类中含有一个可以操作头部的元素,类的属性可以是一个LinkedList集合对象
3、通过定义方法操作属性的首元素
代码示例:
import java.util.LinkedList;
public class GenericClass<T> {
//定义属性 类的属性可以是一个集合对象
LinkedList<T> list = new LinkedList<T>();
//头部添加元素
public void addFirst(T t) {
list.addFirst(t);
}
//删除头部元素
public void removeFirst() {
list.remove();
}
@Override
public String toString() {
return "GenericClass [list=" + list + "]";
}
}
测试类:
public class TestGenericClass {
public static void main(String[] args) {
//创建泛型类的实例化对象
GenericClass<String> genericClass = new GenericClass<String>();
genericClass.addFirst("abcd");
genericClass.addFirst("java");
genericClass.addFirst("123");
System.out.println(genericClass);
genericClass.removeFirst();
System.out.println(genericClass);
}
}
泛型的方法
概述:
具有泛型声明的方法就叫做泛型方法
自己在方法上声明一个未来的数据类型,之后方法中就可以使用这个数据类型
格式:
普通方法:
修饰符 <泛型> 返回值类型 方法名(参数列表) {
方法体
}
静态方法:
修饰符 static <泛型> 返回值类型 方法名(参数列表) {
方法体
}
注意事项:
1、方法上面一旦声明泛型 方法体中就可以使用该泛型
2、静态方法的泛型声明位置必须在static和返回值类型之间
3、普通方法没有声明泛型,可以使用类的泛型
4、静态方法没有声明泛型,不可以使用类的泛型,方法中就不可以出现泛型使用,要想使用泛型必须自己声明
泛型方法的泛型的实例化时机:
方法被调用传入实际参数的时候
泛型被实例化的时候,泛型就消失
练习
定义一个方法,可以交换任意类型数组中的两个元素
分析:
交换数组两个元素的位置 方法从外界得到 指定的数组 指定两个
元素的索引值
代码示例
import java.util.Arrays;
public class GenericMethod2 {
public static void main(String[] args) {
Integer[] arr = {10,20,30};
GenericMethod2 genericMethod2 = new GenericMethod2();
exchangeArr(arr, 0, 2);
System.out.println(Arrays.toString(arr)); //[30, 20, 10]
}
//泛型方法的练习方法
public static <T> void exchangeArr(T[] ts , int index1, int index2) {
T temp = ts[index2];
ts[index2] = ts[index1];
ts[index1] = temp;
}
}
泛型接口:
在接口名后面声明泛型的接口就是泛型接口
泛型实例化时机:
1、创建实现类对象的时候必须实例化接口中的泛型【实现类实现接口时接口没有实例化泛型 继承给类】
2、子接口或实现类实现的时候接口本身实例化泛型
代码示例:
接口:
public interface InterfaceA<T> {
void show(T t);
}
实现类1:
public class ClassA implements InterfaceA<String>{
@Override
public void show(String t) {
}
}
实现类2:
public class ClassB<T> implements InterfaceA<T>{
// 实现类实现接口的时候没有实例化 接口没有实例化泛型 泛型就被类继承 需要声明同样类型的泛型
//泛型想要实例化: 在创建实现类对象的时候实例化
@Override
public void show(T t) {
}
public static void main(String[] args) {
//创建实现类对象时候,实例化泛型
ClassB<String> classB = new ClassB<String>();
}
}
泛型通配符
概述:
给别人传给我们的泛型匹配的一个符号【对泛型的一种实例化】,使用<?>表示泛型通配符
比如:
Collection接口中的方法:boolean containsAll(Collection<?> c);
方法接收一个传入的集合,集合里面肯定有具体的数据,数据对应就有具体的数据类型,未来传什么数据类型数据的集合进来不知道,但是方法准备容器c来接收, c的类型知道是Collection, 但是接收集合的数据类型不知道,传入的集合的数据的数据类型,其实就是对泛型的实例化,c容器接收的时候不知道具体的实例化的数据类型,就使用?代替了实例化的具体类型
泛型通配符的权限:
上限:<? extends 类型>
传入参数的实例化类型只能是类型自己或类型的子类类型
下限:<? super 类型>
传入参数的实例化类型只能是类型自己或类型的父类类型
代码示例:
import java.util.ArrayList;
public class GenericWildcardDemo {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<String> list2 = new ArrayList<String>();
ArrayList<Number> list3 = new ArrayList<Number>();
ArrayList<Object> list4 = new ArrayList<Object>();
show(list1); // ? 就是Integer
show(list2); // ? 就是String
show(list3); // ? 就是Number
show(list4); // ? 就是Object
show01(list1); // ? 实例化的类型是Integer 是Number的子类 ? 就匹配上了
//show01(list2); // ? 要求必须是Number家族 子类和自己 泛型的实例化类型是String,所以报错
show01(list3); // ? 实例化的类型是Number ? 就匹配上了
//show01(list4); // ? 实例化的类型是Object 太顶层了 ?就匹配不上了
// show02(list1);// ? 实例化的类型是Integer super是找Number自己及父类 ?就匹配不上了
// show02(list2);// ? 要求必须是Number家族 父类和自己 泛型的实例化类型是String
show02(list3);// ? 实例化的类型是Number 正好 ? 就匹配上了
show02(list4);// ? 实例化的类型是Object 是Number的顶层父类 ? 就匹配上了
}
public static void show(ArrayList<?> list) {
}
//传入参数的实例化类型只能是类型自己或类型的子类类型
public static void show01(ArrayList<? extends Number> list) {
}
//传入参数的实例化类型只能是类型自己或类型的父类类型
public static void show02(ArrayList<? super Number> list) {
}
}