Java泛型简介
泛型是Java SE 1.5新特性,泛型的本质是参数化类型,即操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称之为泛型类、泛型接口、泛型方法。Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况下,通过对Object的引用来实现参数的“任意化”,但这样带来的缺点是要做显示的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译时期无法发现,在运行期才会出异常,这是一个安全隐患。
简而言之,泛型的好处就是在编译期检查类型安全,并且所有的强制转换都是隐式的,提高了代码的重用率。
Java泛型实例
泛型类实例:
public class GenericsDemo {
public static void main(String args[]) {
// 泛型类使用
Utils<Person> u = new Utils<Person>();
u.setObject(new Person("zhangsan", 20));
Person p = u.getObject();
System.out.println("name:" + p.name + "; age: " + p.age);
// 泛型方法使用
Utils.print("zhangsan");
Utils.print(20);
}
}
// 泛型类定义
class Utils<T> {
private T t;
public void setObject(T t) {
this.t = t;
}
public T getObject() {
return t;
}
// 泛型方法定义
public static <E> void print(E e) {
System.out.println("print: " + e);
}
}
class Person {
public String name;
public Integer age;
Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
泛型类:在类名后面加上<T>指定类型,T即可在该类中作为一个固定类型来用。在没有泛型前是在公共类中定义Object对象(因为Object是所有类的父类),再通过父类引用指向子类对象来对子类对象进行处理,然后返回这个父类引用,再将这个父类引用的对象强转成我们需要的子类对象。这个过程就涉及到强制转换的问题,当然如果程序员能够很精准的使用对象是没有问题的;但如果程序员在对象的转换时有错误,则在编译期无法提示错误,只有到实际运行时才会出现异常。而使用泛型类则在定义使用时就需要指定类型,如果后面使用的类型存在问题则在编译期就能提示错误,这显然优于类型的强转(软件问题发现的越早,损失相对也就越小)。还有就是泛型的强转是隐式的,不须要显示调用,简化了代码。
泛型方法:上例中还定义了一个泛型方法,泛型方法时在“返回值”前用<E>指定类型,E在该方法中也可作为一个固定类型来使用。另外注意一点,静态方法不能使用泛型类指定的类型T,泛型类的T是在实例化对象时才指定的。T和E只是一个标识符,用其它名称均可,只是大家习惯使用T和E。
泛型接口实例:
public class GenericsDemo {
public static void main(String args[]) {
// 泛型类和接口的应用
InterImpl<String> i = new InterImpl<String>();
i.show("hello world!");
Inter<Integer> ii = new InterImpl<Integer>();
ii.show(365);
}
}
// 泛型接口
interface Inter<T> {
void show(T t);
}
// 泛型类实现泛型接口,当然这里也可以用指定类型来实现泛型接口
class InterImpl<T> implements Inter<T> {
public void show(T t) {
System.out.println("show: " + t);
}
}
接口本身是定义规则,通过类来实现功能的。只有在接口上也定义了泛型,子类在实现时才能对其进行重写,从而获取泛型类的优势。
Java泛型高级应用
这里使用到了一个?的特殊符号,先解释下?:表示通配符,对不确定类型的一种替代。通配符时什么东西,我们不是很懂,但对不确定类型的一种替代这个应该见过吧。在上面讲到的泛型类中,我们对不确定类型不就是通过T来替代的么。简而观之:?和T在功能上有重叠,那为什么有了T还再弄个?出来呢。先看一段实例。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericsDemo {
public static void main(String args[]) {
// 测试数据
List<Person> pl = new ArrayList<Person>();
pl.add(new Person("person01", 10));
pl.add(new Person("person02", 20));
pl.add(new Person("person03", 30));
List<Student> sl = new ArrayList<Student>();
sl.add(new Student("student01", 10, 10.1F));
sl.add(new Student("student02", 20, 20.1F));
sl.add(new Student("student03", 30, 30.1F));
List<Worker> wl = new ArrayList<Worker>();
wl.add(new Worker("worker01", 10, 10.1F));
wl.add(new Worker("worker02", 20, 20.1F));
wl.add(new Worker("worker03", 30, 30.1F));
// T
Person pt = showT(pl);
System.out.println("name: " + pt.name + "; age: " + pt.age);
Student st = showT(sl);
System.out.println("name: " + st.name + "; age: " + st.age + "; grade: " + st.grade);
Worker wt = showT(wl);
System.out.println("name: " + wt.name + "; age: " + wt.age + "; grade: " + wt.salary);
showT(new ArrayList<Object>());
// ?
show(pl);
show(sl);
show(wl);
show(new ArrayList<Object>());
// 泛型上限
showUp(pl);
showUp(sl);
showUp(wl);
// showUp(new ArrayList<Object>());// 此处无法编译通过,因为超出了泛型的上限,非Person或子类无法接收
// 泛型下限
showDown(sl);
showDown(pl);
// showDown(wl); // 此处编译失败,因为超出了泛型的下限,非Student或父类无法接收
showDown(new ArrayList<Object>());
}
// T
public static <T> T showT(List<T> pList) {
Iterator<T> it = pList.iterator();
while(it.hasNext()) {
T t = it.next();
// 此时t也只能使用Object对象的方法
System.out.println("name: " + t.toString());
return t;
}
return null;
}
// ?
public static void show(List<?> pList) {
Iterator<?> it = pList.iterator();
while(it.hasNext()) {
// 返回的是Object对象
Object obj = it.next();
System.out.println("show ?: " + obj);
}
}
// 泛型上限
public static void showUp(List<? extends Person> pList){
Iterator<? extends Person> it = pList.iterator();
while(it.hasNext()) {
// 使用泛型上限时,List中的最顶层类指能是Person,所以返回父类引用对象
Person p = it.next();
// 此时p能使用Person对象的方法
System.out.println("name: " + p.name + "; age: " + p.age);
}
}
// 泛型下限
public static void showDown(List<? super Student> pList){
Iterator<? super Student> it = pList.iterator();
while(it.hasNext()) {
// 使用泛型下限时,对List中的类型不确定,所以返回所有类的基类Object对象
Object obj = it.next();
// 此时p只能使用Object对象的方法
System.out.println("showDown: " + obj);
}
}
}
// 测试支持类
class Person {
public String name;
public Integer age;
Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
class Student extends Person{
public Float grade;
Student(String name, Integer age, Float grade) {
super(name, age);
this.grade = grade;
}
}
class Worker extends Person{
public Float salary;
Worker(String name, Integer age, Float salary) {
super(name, age);
this.salary = salary;
}
}
T与?简单对比:
很明显对于使用T和?,在接收参数上两者功能基本类似,都能接收任意类型的集合对象;对对象的操作,两者都是将对象默认成Object类型,只能调用Object对象的方法;在返回值上,T可以返回一个T类型的对象,而?只能返回一个Object对象。在这个地方应该对T是一种类型,?是一种通配符有点理解了吧;T作为类型,也就能定义返回值的类型,而?是通配符,只是暂时用来替代某一对象,也就不能定义返回值的类型了。
泛型上限:
当<? extends Person>时,表示此时接收的参数是有上限的,该实例中就只能接受Person类以及Person的子类;对对象的操作,是将对象转换为Person对象,能调用Person中的方法对对象进行处理;返回值是Person对象引用。
泛型下限:
当<? super Student>时,表示此时接收的参数是有下限的,该实例中就只能接受Student类以及Student的父类;对对象的操作,是将对象转换为Object对象,只能调用Object对象中的方法;返回值是Object对象的引用。
这里对泛型的创建和使用有了个基本的介绍。关于泛型的使用,首先是要理解使用泛型的好处和局限性(好处:避免了对Object的强转,提高了安全性;局限性:在内部操作时不能使用对象特有的方法),就知道在什么时候去合理的设计使用泛型。