泛型 Generics
泛型是JDK1.5及以上才可以使用的特性/语法,它的本质是 类型参数化(Parameterized by types).
1.理解泛型
在声明一个类、接口、方法的时候,需要涉及到到一个问题:要给属性确定一个类型,或者给方法的返回值确定一个类型,或者给方法的参数确定一个类型。
之前,定义类、接口、方法的时候,上面所描述的类型都是直接写死,不会变化的。
现在,希望这些类型都不写死,在将来使用的时候,可以通过传参的方式,最后确定到底这些属性、方法的返回值、方法的参数是什么类型。
2.泛型的作用
这里使用泛型之后:
可以让编译器帮我们做类型安全检查
不需要做类型的强制转换,也就不会有类型转换异常了
提供代码的可读性
List等泛型接口的使用更加灵活,可以通过传参来确定集合中元素的类型是什么。
3.泛型类、泛型接口、泛型方法
如果泛型参数定义在类上面,那么这个类就是一个泛型类
如果泛型参数定义在接口上面,那么这个接口就是一个泛型接口
如果泛型参数定义在方法上面,那么这个方法就是一个泛型方法
例如:
//泛型类
public class Point<T>{...}
//泛型接口
public interface Action<T>{...}
//泛型方法
public class Student{
public <T> T test(T t){
//..
}
}
注意,<>就是用来声明的泛型的标识
通配符?
例如:
public void test1(Collection<Integer> c){
}
public void test2(Collection<String> c){
}
public void test3(Collection<Object> c){
}
注意:
test1方法【只能】接收泛型是Integer类型的集合对象
test2方法【只能】接收泛型是String类型的集合对象
test3方法【只能】接收泛型是Object类型的集合对象
由于泛型的类型之间没有多态,所以=号俩边的泛型类型必须一致。
例如:
public void test4(Collection<?> c){
}
注意,这时候test4方法中的参数类型,使用了泛型,并且使用问号来表示这个泛型的类型,这个问号就是通配符,可以匹配所以的泛型类型
所以,test4方法可以接收 泛型是任意类型的 集合对象
例如:
t.test4(new ArrayList<String>());
t.test4(new ArrayList<Integer>());
t.test4(new ArrayList<Object>());
t.test4(new ArrayList<任意类型>());
使用通配符?所带来的问题:
注意,使用?通配之后,就不能在往集合中添加数据了。只能添加null。
泛型的边界(上限和下限)
public void test1(Collection<?> c){
for(Object obj:c){
System.out.println(obj);
}
}
注意,这里直接使用通配符?,表示将来可以接收任意类型的泛型集合对象
使用extends关键字可以设置泛型的上限。
这个表示的范围是比较广泛的,很多时候,我们希望把这个范围再限定一下,缩小点范围。
public void test1(Collection<? extends Number> c){
for(Object obj:c){
System.out.println(obj);
}
}
这个时候方法的参数就表示,只能接收泛型类型是Number或者Number【子类型】的集合对象。
使用super关键字可以设置泛型的下限。
public void test1(Collection<? super Number> c){
for(Object obj:c){
System.out.println(obj);
}
}
这个时候方法的参数就表示,只能接收泛型类型是Number或者Number【父类型】的集合对象。
例如:
List<Number> list3 = new ArrayList<Number>();
List<Object> list4 = new ArrayList<Object>();
List<java.io.Serializable> list5 = new ArrayList<>();
//Number继承了Object并实现了Serializable接口
//Object和Serializable都属于Number的父类型
//这三个代码编译都是通过的
t.test(list3);
t.test(list4);
t.test(list5);
总结:
使用extends可以定义泛型的【上限】,这个就表示将来泛型所接收的类型【最大】是什么类型。可以是这个最大类型或者它的【子类型】。
使用super可以定义泛型的【下限】,这个就表示将来泛型所接收的类型【最小】是什么类型。可以是这个【最小类型】或者它的【父类型】。