泛型
介绍
一种未知的数据类型,不知道用什么数据类型的时候,可用泛型
泛型也可以看作是一个变量,用来接收数据类型
E e: Element 元素
T t: Type 类型
当创建集合对象的时候,就会确定泛型的数据类型
不使用泛型
好处:
默认类型是Object类,能够存储任意类型的数据
坏处:
不安全,引发异常
ArrayList list = new ArrayList();
list.add("aaa");
list.add(111);
Iterator it = list.iterator();
while (it.hasNext()){
Object next = it.next();
System.out.println(next);
// 引发异常,不能把Integer类型转为String类型
String s=(String) next;
System.out.println(s.length());
使用泛型
好处:
避免类型转换的麻烦,存储什么类型,取出的就是什么类型
把运行期异常,提升到了编译期
坏处:
泛型是什么类型,只能存储什么类型的数据
泛型的类
定义:
格式:
修饰符 class 类名<泛型>{
// 成员变量
private 泛型 成员变量名;
// 构造方法
public 泛型 get...(){
return ...;
}
public void set... (泛型 ...){
this... = ...;
}
}
public class GenericClass<E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
测试:
public static void main(String[] args) {
// 不写泛型默认是Object
GenericClass gc = new GenericClass();
gc.setName("只能是字符串");
Object name = gc.getName();
// 使用泛型
GenericClass<Integer> gc2 = new GenericClass<>();
gc2.setName(123);
Integer name1 = gc2.getName();
System.out.println(name1);
GenericClass<String> gc3 = new GenericClass<>();
gc3.setName("da");
String name2 = gc3.getName();
System.out.println(name2);
}
泛型的接口
定义:
格式:
修饰符 interface 接口名 <I/*泛型,可使用任意字母*/> {
抽象方法…(I i);
}
public interface GenericInterface<I> {
public abstract void method(I i);
}
实现一
定义接口的实现类,实现接口,指定接口的泛型
public class GenericInterfaceImpl1 implements GenericInterface<String> {
@Override
public void method(String s) {
System.out.println(s);
}
}
实现二
接口是用什么泛型,实现类就是用什么泛型。
public class GenericInterfaceImpl2<I> implements GenericInterface<I>{
@Override
public void method(I i) {
System.out.println(i);
}
}
测试:
public static void main(String[] args) {
GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
gi1.method("字符串");
GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
gi2.method(123);
GenericInterfaceImpl2<String> gi3 = new GenericInterfaceImpl2<>();
gi3.method("avc");
}
泛型的方法
定义含有泛型的方法: 定义在方法的修饰符和返回值类型之间
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
}
public class GenericMethod {
// 定义一个含有泛型的方法
public <M> void method01(M m){
System.out.println(m);
}
// 定义一个含有泛型的静态方法
public static <S> void method02(S s){
System.out.println(s);
}
}
测试:
public static void main(String[] args) {
GenericMethod gm = new GenericMethod();
gm.method01("aaa");
gm.method01(01);
gm.method01(true);
GenericMethod.method02("daa");
GenericMethod.method02(11);
}
通配符
泛型通配符:
不知道是用什么类型来接受的时候,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能向该集合中存储数据
使用方法:
不能创建对象使用
只能作为方法的参数使用
例子:
定义一个方法,能够遍历所有类型的ArrayList集合这时候我们不知道ArrayList集合使用什么数据类型,可以使用泛型通配符?接受数据类型
public static void main(String[] args) {
ArrayList<Integer> list01 = new ArrayList<>();
list01.add(1);
list01.add(2);
ArrayList<String > list02 = new ArrayList<>();
list02.add("a");
list02.add("ab");
printArray(list01);
printArray(list02);
// ArrayList<?> list02 = new ArrayList<>();
}
public static void printArray(ArrayList<?> list){
// 使用迭代器便利
Iterator<?> it = list.iterator();
while (it.hasNext()){
Object o = it.next();
System.out.println(o);
}
}
上下限定
泛型的上限限定: ? extends E 代表使用的泛型只能是E类型的子类/本身
泛型的下限限定: ? super E 代表使用的泛型只能是E类型的父类/本身
例子:
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<>();
Collection<String> list2 = new ArrayList<>();
Collection<Number> list3 = new ArrayList<>();
Collection<Object> list4 = new ArrayList<>();
getElement1(list1);
// getElement1(list2); // 报错
getElement1(list3);
// getElement1(list4); // 报错
// getElement2(list1); // 报错
// getElement2(list2); // 报错
getElement2(list3);
getElement2(list4);
}
// 子类或者本身也就是Integer和Number
public static void getElement1(Collection<? extends Number> coll) {}
// 父类或者本身也就是Object和Number
public static void getElement2(Collection<? super Number> coll) {}
类与类之间的关系:
Integer extends Number extends Object
String extends Object
getElement1 参数列表使用的是 ?extends Number,需要遵循泛型的上先限定,
Number的子类有Integer,因此使用的泛型只能是其子类Integer和本身Number,
故list2,list4报错
getElement1 参数列表使用的是 ?super Number,需要遵循泛型的下线限定,
Number的父类有Object,因此使用的泛型只能是其父类Object和本身Number,
故list1,list2报错