——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
泛型:
在新建和声明集合时,在集合类型的后边加上类型,那么就只有声明的类型的元素才能加进该集合里面。
ArrayList a1 = new ArrayList();
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。这时候我们要把对象取出来的时候就要进行类型转换才能使用,这样容易出现安全隐患,也很麻烦。
使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,更方便。
什么时候定义泛型:在JDK API里类后面看到<>就要定义泛型。
引入泛型以后,前面讲解反射的代码就可以改写成如下形式了,这种情况下创建实例对象时不需要类型转换:
public class GenericTest {
public static void main(String[] args) throws Exception {
//在构造函数类上声明该类只能放入String类型元素
Constructor constructor = String.class.getConstructor(StringBuffer.class);
//这时候通过newInstance方法得到的不再是Object类而是String类对象
String str = constructor.newInstance( new StringBuffer("abc" ));
System. out.println(str.charAt(2));
//结果:c
}
}
编译生成的字节码会去掉泛型的类型信息,所以只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
ArrayList类定义和ArrayList类引用中涉及如下术语:
- 整个称为ArrayList泛型类型
- ArrayList中的E称为类型变量或类型参数
- 整个ArrayList称为参数化的类型
- ArrayList中的Integer称为类型参数的实例或实际类型参数
- ArrayList中的<>念着typeof
- ArrayList称为原始类型
自定义泛型:
定义在方法上:
当方法中要操作的引用数据类型不确定的时候,可以使用自定义泛型。
用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
只有引用类型才能作为泛型方法的实际参数
class Demo
{
//将自定义泛型定义在方法上,并用作参数
public void show(T t)
{
System.out.println("show:"+t);
}
//也可以当作数组参数来用
public void print(Q[] q)
{
for(int i = 0; i < q.length; i++){
System.out.println("print:"+q[i]);
}
}
}
class GenericDemo4
{
public static void main(String[] args)
{
Demo d = new Demo();
//这时可以使用任意类型数据
d.show("haha");
d.show(4);
//打印数组
String[] s = {"safa","hehe","san","si"};
d.print(s);
}
}
在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:
public static< K,V> V getValue(K key) { return map.get(key);}
定义在类上:
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型
//将泛型定义在类上
class Utils
{
//使用泛型参数化类型变量
private QQ a;
//使用泛型当作方法参数
public void setObject(QQ q)
{
this.q = q;
}
//使用泛型当作返回值类型
public QQ getObject()
{
return q;
}
}
class GenericDemo3
{
public static void main(String[] args)
{
Utils u = new Utils();
u.setObject(new Student());
Worker w = u.getObject();;
}
}
静态泛型:
特殊之处:
- 静态方法不可以访问类上定义的泛型。
- 如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
class Demo
{
public static void method(W t)
{
System.out.println("method:"+t);
}
}
定义在接口上:
//泛型定义在接口上
interface Inter<T>
{
void show(T t);
}
class InterImpl<T> implements Inter<T>
{
public void show(T t)
{
System.out.println("show :"+t);
}
}
class GenericDemo5
{
public static void main(String[] args)
{
InterImpl i = new InterImpl();
i.show(4);
}
}
泛型限定:
? 通配符。也可以理解为占位符。
泛型的限定:
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限
注意:
限定通配符总是包括自己。
class GenericDemo6
{
public static void main(String[] args)
{
//Person集合
ArrayList al = new ArrayList();
al.add(new Person("abc1"));
al.add(new Person("abc2"));
al.add(new Person("abc3"));
//printColl可以接收Person集合
printColl(al);
//Person的子类Student集合
ArrayList al1 = new ArrayList();
al1.add(new Student("abc--1"));
al1.add(new Student("abc--2"));
al1.add(new Student("abc--3"));
//也可以接收Person的子类Student集合
printColl(al1); //ArrayList extends Person> al = new ArrayList();error
}
//定义一个接收Person及其子类集合的打印方法
public static void printColl(Collection extends Person> al)
{
Iterator extends Person> it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
}
除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如
< V extends Serializable & cloneable> void method(){},这时V需要实现Serializable和cloneable两个接口。
通过反射获得泛型的实际类型参数
import java.lang.reflect.*;
import java.util.*;
public class GenericTest {
public static void main(String[] args) throws Exception {
//通过方法名跟参数Class类获取方法对象
Method method = GenericTest.class.getMethod("applyVector" , Vector.class);
//获取方法的形参数组
Type[] types = method.getGenericParameterTypes();
//获取第一个形参的参数化类型
ParameterizedType pType = (ParameterizedType)types[0];
//得到该类型的类或接口
System.out.println(pType.getRawType());
//结果:class java.util.Vector
//得到泛型参数的实际类型
System. out.println(pType.getActualTypeArguments()[0]);
//结果:class java.util.Date
}
public static void applyVector(Vector v1){
}
}