1、泛型的概念
泛型的出现,可以让我们少些代码,它的主要作用是解决类型的安全问题,不会因为将对象置于某个容器中而失去其类型。在泛型出现之前,Java提供了对Object类型引用的任意操作,即向上转型和向下转型,上转型没问题,但是向下转型这种强制类型转换是存在隐患的,因此Java提供了泛型机制。
Java泛型(generics)是JDK5中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
泛型并不是一种数据类型,而是一种特殊语法,对接收的数据类型进行了规定。如Map<String,String> map = new HashMap()。只允许字符串类型数据作为Map集合的value值,不接受其他数据类型。使用泛型的好处是在代码书写过程中可提高效率和数据类型安全,规避因数据类型不符合要求而造成的编译错误,最突出的是避免了数据对象强制转换,支持动态下确定数据类型。通常在集合中使用泛型。
以一个例子说明。
定义一个实体类:
public class T1 {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
测试:
public static void main(String[] args) {
T1 t = new T1();
// 上转型
t.setObj(new Boolean(true));
System.out.println(t.getObj());
T1 t2 = new T1();
t2.setObj(new Float(25.5));
// 强制转换
Integer res = (Integer) t2.getObj();
System.out.println(res);
}
执行,控制台:
类型转换错误,Float类型不能强制转换成Integer类型,这是不允许的,但是编译的时候并没有报错,这时候可以用泛型有效的解决这种向下转型的类型安全问题。
2、定义泛型类
泛型类的定义很简单,语法如下:
类名 <T>
使用泛型的好处就是不用再进行强制转换了,也就不存在发生类型转换错误。以下用例子说明。
定义一个泛型类T2:
/*
* 定义泛型类
* 类型用T表示
*/
public class T2<T> {
// 这个obj没有定义成具体类型
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
测试:
public static void main(String[] args) {
// 传入Boolean类型作为参数
T2<Boolean> t0 = new T2<>();
t0.setObj(true);
Boolean b = t0.getObj();
System.out.println(b);
// 传入Float类型作为参数
T2<Float> t2 = new T2<>();
t2.setObj(25.5f);
Float f = t2.getObj();
System.out.println(f);
}
控制台:
3、声明多个类型
在定义泛型类的时候,可以同时声明多个类型,语法如下:
类名 <类型1,类型2>
以下代码说明:
public class T3<Integer, Boolean> {
private Boolean single;
private Integer age;
public Boolean getSingle() {
return single;
}
public void setSingle(Boolean single) {
this.single = single;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
测试:
public static void main(String[] args) {
T3<Integer, Boolean> t = new T3<>();
t.setSingle(true);
t.setAge(25);
System.out.println("年龄:" + t.getAge());
System.out.println("是单身吗?" + t.getSingle());
}
控制台:
4、声明数组类型
在定义泛型的时候可以声明为数组类型,但是不能使用泛型机制来建立数组实例。代码如下:
public class T4<T> {
//这个数组的类型为T
private T[] arrays;
public T[] getArrays() {
return arrays;
}
public void setArrays(T[] arrays) {
this.arrays = arrays;
}
}
测试:
public static void main(String[] args) {
T4<Integer> t = new T4<>();
Integer[] num = new Integer[] {1, 4, 3, 123, 45 };
t.setArrays(num);
System.out.println("数组中的元素如下:");
for (Integer i : t.getArrays()) {
System.out.print(i + "\t");
}
System.out.println();
System.out.println("--------------------------------");
T4<String> t2 = new T4<>();
String[] str = new String[] {"yan", "cheng", "zhi", "yun" };
t2.setArrays(str);
System.out.println("数组中的元素如下:");
for (String s : t2.getArrays()) {
System.out.print(s + "\t");
}
}
控制台:
5、声明集合类的容器元素
可以使用K代表键,V代表值。如下:
public class T5<K, V> {
public Map<K, V> map = new HashMap<>();
// 设置put方法
public void put(K k, V v) {
map.put(k, v);
}
// 设置get方法
public V get(K k) {
return map.get(k);
}
public Map<K, V> getMap() {
return map;
}
public void setMap(Map<K, V> map) {
this.map = map;
}
}
测试:
public static void main(String[] args) {
T5<String, Integer> t = new T5<>();
for (int i = 1; i <= 5; i++) {
t.put("第" + i + "个元素", i);
}
Set<Entry<String, Integer>> set = t.getMap().entrySet();
for (Entry<String, Integer> entry : set) {
System.out.println(entry.getKey() + "\t" + entry.getValue() + "\n");
}
}
控制台:
事实上,这种是多余的,因为Java的集合框架已经泛型化了,直接使用就行了,不用这么麻烦的定义。
6、限制泛型可用类型
默认的可以使用任何的类型来实例化一个泛型对象,但是也可以做出限制,规定可以使用哪些类型来实例化对象。语法如下:
类名 <T extends 类型>
使用泛型限制以后,只有继承了规定的类型或实现了这个接口的类才能作为泛型的参数,以下代码说明:
/*
* 限制泛型参数类型
*/
public class T6<T extends List> {
public static void main(String[] args) {
// ArrayList实现了List接口,可以作为泛型参数
T6<ArrayList> t = new T6<>();
// LinkedList实现了List接口,可以作为泛型参数
T6<LinkedList> t2 = new T6<>();
// Vector实现了List接口,可以作为泛型参数
T6<Vector> t3 = new T6<>();
// 不接受Integer类型作为泛型参数
// T6<Integer> t4 = new T6<>();
// 不接受String类型作为泛型参数
// T6<String> t4 = new T6<>();
}
}
7、使用类型通配符
在泛型机制中,提供类的通配符?。通配符应用较少,有需求时才使用,不必要时可不用,采用泛型可满足要求即可。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("yan");
list.add("cheng");
list.add("zhi");
List<?> list2 = list;
System.out.println("list2集合大小为:" + list2.size());
for (Object o : list2) {
System.out.print(o.toString() + "\t");
}
System.out.println();
List<Integer> list3 = new ArrayList<>();
System.out.println("list3集合大小为:" + list3.size());
//list2集合会被覆盖掉
list2 = list3;
System.out.println("list2集合大小为:" + list2.size());
}
8、泛型接口
也可以定义泛型的接口,像定义泛型类一样。如下:
/*
* 定义泛型接口
*/
public interface TestDao<T> {
void save(T t);
}
接口的实现类:
public class TestDaoImpl<T> implements TestDao<T> {
@Override
public void save(T t) {
// TODO Auto-generated method stub
}
}