泛型
泛型的定义
什么是泛型,看表面的意思,泛型就是指广泛的、普通的类型。在java中是指把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。
可一看一个简单的例子:
List<String> strings = new ArrayList<String>();
strings.add("a String");
String aString = strings.get(0);
可以看到,通过菱形语法(’<>’)可以将List内元素的类型限定为String类型。
需要注意的是<>内的类型只能是引用类型,当然对于基本类型,可以使用对应的包装类型。
使用泛型的好处
(1)首先就像上面那个例子一样,使用泛型能够限定集合中,如List, Set中元素的类型,保证一个集合中只有一个类型。(这样也就能够使用增强for来遍历集合)
(2)程序也能更加健壮(只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常)
泛型的基础
(1)泛型类
泛型类也就是把泛型定义在类上,这样用户在使用类的时候才把类型给确定下来。
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
可以看到上面这个程序,在使用时如果定义了类型,那么在使用时就可以不用进行强制类型转换,直接就可以得到一个T类型的对象。
(2)泛型方法
有时候只关心某个方法,那么使用泛型时可以不定义泛型类,而是只定义一个泛型方法,如下
public <T> void show(T t) {
System.out.println(t);
}
需要注意一下定义的格式,泛型必须得先定义才能够使用。
(3)继承关系
泛型类在继承时,可以明确父类(泛型类)的参数类型,也可以不明确。
现在我们有如下的泛型类
//泛型类
public interface Inter<T> {
public abstract void show(T t);
}
①明确类型
//在实现泛型类时明确父类的类型
public class InterImpl implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
②不明确类型
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
自定义泛型
泛型字母
形式类型参数(formal type parameters)即泛型字母
命名泛型字母可以随意指定,尽量使用单个的大写字母(有时候多个泛型类型时会加上数字,比如T1,T2)
常见字母(见名知意)
T Type
K V Key Value
E Element
当类被使用时,会使用具体的实际类型参数(actual type argument)代替
泛型类
只能用在成员变量上,只能使用引用类型
泛型接口
只能用在抽象方法上
泛型方法
返回值前面加上 <T>
public class Student<T> {
private T javase;
//private static T javaee; // 泛型不能使用在静态属性上
public Student() {
}
public Student(T javase) {
this();
this.javase = javase;
}
public T getJavase() {
return javase;
}
public void setJavase(T javase) {
this.javase = javase;
}
}
/**
* 自定义泛型的使用
* 在声明时指定具体的类型
* 不能为基本类型
* @author Administrator
*
*/
class Demo02 {
public static void main(String[] args) {
//Student<int> Student = new Student<int>(); //不能为基本类型,编译时异常
Student<Integer> student = new Student<Integer>();
student.setJavase(85);
System.out.println(student.getJavase());
}
}
类型通配符
(1)无界
类型通配符我感觉上和泛型方法差不多,只是不用在使用前进行定义,例子如下:
public void processElements(List<?> elements){
for(Object o : elements){
System.out.println(o);
}
}
"?"可以接收任何类型。
(2)上界
public void processElements(List<? extends A> elements){
for(A a : elements){
System.out.println(a.getValue());
}
}
这种情况下能够接收A类或者A类的子类。
(3)下界
public static void insertElements(List<? super A> list){
list.add(new A());
list.add(new B());
list.add(new C());
}
这种情况下能够接收A类或者A类的父类
(4)类型通配符和泛型方法
我认为这两种方式是差不多的,不过在使用时,如果参数之间是有依赖关系的,那么可以使用泛型方法,否则就使用类型通配符。(如果一个方法的返回值、某些参数的类型依赖另一个参数的类型就应该使用泛型方法,因为被依赖的类型如果是不确定的?,那么其他元素就无法依赖它)。
(5)开发相关
泛型通配符< ? extends T >来接收返回的数据,此写法的泛型集合不能使用 add 方 法, 而< ? super T >不能使用 get 方法,做为接口调用赋值时易出错。
这个怎么来理解呢,当我们使用extends时,我们可以读元素,因为元素都是A类或子类,可以放心的用A类拿出。
当使用super时,可以添加元素,因为都是A类或父类,那么就可以安全的插入A类。