泛型的实质是将数据的类型参数化,在类、接口、方法中定义后,分别被称为:泛型类、泛型接口、泛型方法。泛型类、泛型接口和泛型方法在定义时都是在名字后面加<T>。
如:集合框架
1、泛型类
public class FanXing<T> {// 定义泛型类,在后面加<T>,T是类型参数 private T obj; public T getObj() {//泛型方法 return obj; } public void setObj(T obj) { this.obj = obj; } }
public class FanXingDemo { public static void main(String[] args) { FanXing<String>name=new FanXing<String>();//创建泛型类FanXing<T>对象 FanXing<Integer>age=new FanXing<Integer>(); name.setObj("Tom"); age.setObj(21);//自动装箱 System.out.println(name.getObj());//自动拆箱 System.out.println(age.getObj()); } }
在实例化泛型类的过程中,必须使用引用数据类型。与方法的参数不同,泛型的参数可以在方法、类和接口中。
2、泛型方法
定义遍历数组的泛型方法:
package pers.zhb; public class Way { public <E> void show(E[] list){ for(int i=0;i<list.length;i++){ System.out.println(list[i]+" "); } } }
测试类:
package pers.zhb; public class Test { public static void main(String[] args) { Integer[] num = { 1, 23, 465, 12, 0 }; String[] str = { "xx", "qw" }; Way way = new Way(); way.show(num); way.show(str); } }
(3)通配符(?)
package pers.zhb.fx; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; public class Fx { public static void main(String[] args) { ArrayList<String> array = new ArrayList<String>(); HashSet<Integer> set = new HashSet<Integer>(); array.add("java"); array.add("hellow"); set.add(71); set.add(20); iterator(array); iterator(set); } public static void iterator(Collection<?> coll) { Iterator<?> it = coll.iterator();//获取集合的实现类对象,病调用集合的iterator() while (it.hasNext()) { System.out.println(it.next()); } } }
在此方法中<?>通配符的使用,使参数的类型没有任何限制。
4、泛型的限定:
定义Animal抽象类:
package pers.zhb.limit; abstract class Animal { public String food; public String colour; abstract void eat(); abstract void colour(); public Animal(String food, String colour) { this.food = food; this.colour = colour; } }
定义Rabbit、Sheep类,实现Animal接口:
package pers.zhb.limit; public class Rabbit extends Animal { public void eat() { System.out.println("小兔子爱吃" + this.food); } public void colour() { System.out.println("小兔子是" + this.colour + "的"); } public Rabbit(String food, String colour) { super(food, colour); } }
package pers.zhb.limit; public class Sheep extends Animal{ public Sheep(String food, String colour) { super(food, colour); } void eat() { System.out.println("羊爱吃"+this.food); } void colour() { System.out.println("羊的颜色是"+this.colour); } }
定义测试类:
package pers.zhb.limit; import java.util.ArrayList; import java.util.Iterator; public class Test { public static void main(String[] args) { ArrayList<Rabbit> r = new ArrayList<Rabbit>(); ArrayList<Sheep> s = new ArrayList<Sheep>(); Rabbit r1 = new Rabbit("草", "白色"); Rabbit r2 = new Rabbit("胡萝卜", "黑色"); Rabbit r3 = new Rabbit("白萝卜", "灰色"); r.add(r1); r.add(r2); r.add(r3); Sheep s1 = new Sheep("青草", "白色"); Sheep s2 = new Sheep("树叶", "白色"); s.add(s1); s.add(s2); Test.showAnimal(r); Test.showAnimal(s);//ArrayList<? extends Animal> 对传入的参数进行限定,集合存储的只能是Animal类的对象,或者他的子类对象 } public static void showAnimal(ArrayList<? extends Animal> array) { Iterator<? extends Animal> it = array.iterator(); while (it.hasNext()) { Animal a = it.next(); a.eat(); a.colour(); } } }
在这个例子中,如果没有对传入迭代器的参数进行限定,那么传入的数据类型是任意的,对程序的安全性构成威胁。
上限通配为:?extends T
下限通配为:? super T
5、泛型的好处
(1)把运行时期的问题提前到了编译期间 ,提高了代码的安全性
在定义集合的时候如果不指定泛型可以添加任意类型的数据,安全性是不足的,例如:在定义泛型的时候将泛型定义为Student类型的,那么该集合就只能存储Student类型的集合
如果使用非泛型编程,如下代码,就有可能在某些情况下会发生异常,因为list数组只要是对象就能存储但是并没有对对象的类型进行限制:
ArrayList list=new ArrayList(); list.Add(20); list.Add("string"); list.Add(new MyClass()); foreach(int i in list) { Console.WriteLine(i); //这里会有个异常,因为并不是集合中的所有元素都可以转化为int }
如果该用泛型编程,则可以避免这种异常,让编译器检查出错误。
List<> list=new ArrayList<Integer>(); list.Add(20); lsit.Add(”string”); //编译时报错,只能报整数类型添加到集合中 list.Add(new MyClass()); //同上
(2)不需要强制类型转换
如果用Object代表任意类型,向下转型要使用强制类型转换,此外,如果向List集合中存储一个String,存储Integer也是没有问题的,因为它是Object类型的
(3)泛型能够将参数的数据类型参数化,在编程中能够实现代码的复用,如:一个方法可以传递Student类型的对象,在使用泛型后也可以传递Teacher类型的对象,就不用写两个方法了