Java中的泛型jdk1.5之后的一个重要的特性。
泛型:英文Generic
1.定义:泛型是
程序设计语言的一种特性。允许程序员在
强类型
程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种
程序设计语言和其
编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种
数据类型。
泛型类是
引用类型,是
堆对象,主要是引入了类型参数这个概念
2.使用jak中的泛型
(1) 声明一个ArrayList的容器对象可以使用泛型,用法是在类名后面加一对尖括号来指定具体的类型化参数
ArrayList<String> arrl=
new
ArrayList<String>();
//这样就指明了在这个ArrayList的容器中只能装类型类型为String的对象
arrl.add(
"sjkfh"
);
arrl.add(
9
);//这里编译器就会报错,因为声明的参数化类型是String类型的对象,不能add整数类型的对象
(2)泛型中的?通配符。使用?通配符就表明了这个泛型可以接受任何类型的参数,例如写一个方法它可以接受任何类型的参数,可以这样定义:
//体验泛型的?通配符,在泛型的尖括号中用问号来指定就不确定具体的类型是什么了,就可以统配任何类型了
//使用?通配符可以使用其他任何参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不可以调用与参数化有关的方法
public
static
void
printColletion(Collection<?> collection){
System.
out
.println(collection.size());
for
(Object obj:collection){
System.
out
.print(obj);
}
}
(3
)?通配符的扩展:
限定通配符的上边界,如:
Vector<?
extends
Number> v1=
new
Vector<Number>();
这里是声明了Vector的对象的泛型,它参数化类型是任意的,但限定了范围,这里表明了它的参数化类型只能是Number以及继承了Number的之类的类型
限定通配符的下边界,如:
Vector<?
super
Integer> v2=
new
Vector<Number>();
同样的这里也定义了泛型的参数化类型,它的范围只能是Integer以及它的父类
3.自定义泛型。
(1)定义泛型的方法,要定义一个方法使它能作用于任何类型的参数可以这样写,在方法的返回值前面加上尖括号<E>如有返回值则应在泛型的后面指定同样的类型E
//定义一个方法 对传入的Object类型的对象转换成其他类型的对象
private
static
<T> T autoConvert(Object obj){
return
(T)obj;
}
//定义一个方法,能打印出任意类型集合中的所有元素
public
static
<T>
void
printColletion1(Collection<T> collection){
System.
out
.println(collection.size());
for
(Object obj:collection){
System.
out
.print(obj);
}
}
(2)定义泛型类,定义泛型类只需要在类名的后面加上尖括号<E>可以了
public
class
GenicDao<E>
需要注意的是:在类中定义的方法的类型必须严格按照类的泛型来定义,同时还不能有static方法,因为在泛型类中
因为在泛型中只能指定了对象,
而静态的方法直接用类名来调用则会混淆。
如果你硬要定义静态方法则要在你定义的泛型方法中自定义泛型
public
static
<E>
void
updeta2(E e){
}
注意这里的E和类中的E是两个不同的概念哈!
4.在java中的泛型它只是给编译器看,在编译器编译变成字节码后,它的具体的参数化类型就没有区别了可以做这样的一个测试
ArrayList<String> arrl=
new
ArrayList<String>();
ArrayList<Integer> arr2=
new
ArrayList<Integer>();
System.
out
.println(arrl.getClass() == arr2.getClass());
//output:true
从输出的结果为true可以看到这两份字节码是一样的,所以证明了泛型只是给编译器看,而编译过后则丢失了指定引用类型的区分。
另外一个泛型中的重要知识点:我们可以通过反射来透过编译器来获取泛型中的实际参数类型。
首先说明一下,我们想通过获得类的字节码来区分一个集合里到底装的是什么类型,这样是行不通的,因为在编译器编译过后出来的字节码已经去掉了实际的参数类型。
另外一种做法是写一个方法
//定义一个方法对泛型中具体的参数类型进行操作
public
static
void
applyGenic(Vector<Date> v1){
}
定义这个方法之后,我没有办法通过v1来获得前面这个变量的类型,但是我们可以通过这个方法来知道他的参数列表的类型
Method applyMethod=GenicTest.
class
.getMethod(
"applyGenic"
, Vector.
class
);
Type[] types=applyMethod.getGenericParameterTypes();
//这里指得到参数的泛型,返回的是一个Type[]数组
ParameterizedType pType=(ParameterizedType)types[0];
//ParameterizedType是Type下面的子类,是参数化类型
System.
out
.println(pType.getRawType());
//getRawType()这里获得是原始的类型
System.
out
.println(pType.getActualTypeArguments()[0]);
//getActualTypeArguments()[0]获得的实际的参数类型
4.小应用案例:
/*对java泛型的综合应用案例:
1.利用HashMap指定一个参数化类型的变量用来存储集合数据HashMap<k,v>指定一个key和value,key和value是一一对应的,现在的问题是要利用迭代来取出其中的key和value
2.由于Map没有实现 Iterable<E> 接口所以不能进行迭代,而Set实现了 Iterable<E> 接口可以进行迭代
3.所以现在思考的问题就是要将HashMap转换为Set,则利用到HashMap中的一个方法entrySet(),返回值是Set,而这里要注意的是Set的类型化参数也是一个泛型
*/
HashMap<String, Integer> maps =
new
HashMap<String, Integer>();
maps.put(
"djkf"
, 33);
maps.put(
"sdfh"
, 25);
maps.put(
"sdkjf"
, 78);
Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();
for
(Map.Entry<String, Integer> entry:entrySet){
//这里要注意对于迭代出来的类型是Map.Entry<String, Integer>,因为Set里面装的就是Map.Entry<String, Integer>
System.
out
.println(entry.getKey() +
":"
+ entry.getValue());
}