---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------
一、泛型定义
泛型就是一种类型限定,是一种类型操作安全机制,解决在运行期间发生的ClassCastException异常,将类型异常转移到了编译期间,让程序员在编码过程就很直观的发现问题,解决类型安全问题。
简单的说,泛型就是一种类型预定义,是一种类型操作安全机制。
二、泛型的特点
1、 泛型是提供给Javac编译器使用的,用来限定参数化的泛型类型以及在集合中存放数据类型的唯一性,在编译期杜绝了数据类型不匹配的问题,当编译通过后,生成的字节码已经去掉了泛型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据,如用发射的方法往集合中添加不同类型的数据。
2、 在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("789");
System.out.println(al.get(1));
ArrayList<Integer> at = new ArrayList<Integer>();
at.add(23);
at.add(3);
System.out.println(at.get(1));
//编译器生成的字节码会去掉泛型的类型信息
System.out.println((al.getClass() == at.getClass()));
//打印结果:true
三、泛型在类上,类中方法,静态方法上,泛型在接口上体现
1、泛型类:
泛型定义在类上当一个类中的多数功能可以操作多种类型的数据,但是此时此刻,只用来操作一种类型的数据,那么可以将泛型定义在类上,这样就泛型就可以作用到所有的成员方法上,类泛型作用范围是整个类中。
2、 泛型方法:
当一个类中的不同方法可能要操作不同的数据的时候,这个时候可以将泛型定义在方法上,这样就每个方法都可以的独立操作自己需要操作的数据类型,当然如果在类上面也定义了泛型,那么类中的方法也可以使用类上的泛型静态方法不能使用类上的泛型,因为静态方法是随着类加载的时候加载,而类上的泛型是在对象创建的时候使用,可是在没创建对象前静态方法就存在了,那么使用类上的泛型就会出错,所以静态方法如果用使用泛型,就只能定义在静态方法上面。
3、静态泛型方法:
静态方法不能使用类上定义的泛型,因为静态属性和方法随着类的加载而加载,而在类上定义的泛型是在创建对象的时候使用,而创建对象之前,静态方法或属性是访问不到泛型所接受的类型的,所以如果静态属性和方法要使用泛型,就必须在其自身定义。
/*
泛型类与泛型方法
如果静态方法定义泛型,必须把泛型定义想方法上;
*/
class student<T>{
//类上的类型与study方法一致
public void study(T t){
System.out.println("t=="+t);
}
//show泛型方法可以是单独类型
public <E> void show(E e){
System.out.println("show====="+e);
}
//如果静态方法定义泛型,必须把泛型定义想方法上;
public static <F>void print(F f){
System.out.println("print:"+f);
}
}
class fanxin1{
public static void main(String[] args){
student<Integer> s =new student<Integer>();
s.study(456);
s.show("String:"+"长生");
s.print("static:"+"ABC");
}
}
4、泛型在接口上
练习代码:
/*
泛型定义在接口上
如果静态方法定义泛型,必须把泛型定义想方法上;
*/
interface Inter<T>{
void show(T t);
}
//实现了接口,也可以不指定具体类型
class InterImpl<T> implements Inter<T>{
public void show(T t){
System.out.println("show :"+t);
}
}
class fanxin{
public static void main(String[] args){
//泛型在使用时传具体类型,
InterImpl<Integer> i=new InterImpl<Integer>();
i.show(4);
}
}
四、泛型的参数化
1. 参数化类型与原始类型的兼容性
a 参数化类型可引用一个原始类型的对象,编译只是报警告,能不能通过编译,是编译器说了算。
如:Collection<String> coll = new Date();
b\ 原始类型可以引用一个参数化类型的对象,编译器报告警告
Collection coll = new Vector<String>(); //编译通过
2. 参数化类型不考虑类型参数的继承关系
Vector<String> v = new Vector<Objec>() ; //编译失败
3. 在创建数组实例时,数组的元素不能使用参数化的类型
Vector<Integer> v[] = new Vector<Integer>[10];//错误的
package cn.itcast.day2;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
public class GenricTest {
/**泛型的加强
* 通过反射,获取集合的实例对象,添加元素
* @param args
* @throws NoSuchMethodException
* @throws SecurityException
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ArrayList<Integer> al=new ArrayList<Integer>();
al.add(8);
ArrayList<String> as=new ArrayList<String>();
as.add("aaa");
//泛型只进行类型检查,提供个编译之前会去掉类型。
//他们的字节码是一个类型的class
System.out.println(al.getClass()==as.getClass());
//通过反射,获取class,再获取getMethod()方法,通过invoke()方法
//进行添加元素
al.getClass().getMethod("add",Object.class).invoke(al, "abc");
System.out.println(al.get(0));
print(al);
//因为Class.forName()返回值:Class<?>
//未知类型不可以指向明确的类型
//Class<String> x=Class.forName("java.lang.String");
Class<?> y=Class.forName("java.lang.String");
}
public static void print(Collection<?> collection){
//collection.add(7);错误的
collection.size();
for(Object obj:collection){
System.out.println(obj);
}
}
}
五、通配符“?”
1. 定义: 任意类型包容,当传入的类型不确定的时候,可以使用通配符号“?”
2. 使用?通配符可以接收任意类型引用,通配符的变量主要作用,也可以调用与参数无关的方法,但不能调用与参数化无关的方 法。
/*ArrayList<String> al=new ArrayList<String>();
al.add("秀and云1");
al.add("秀and云6");
al.add("秀and云3");
ArrayList<Integer> al1=new ArrayList<Integer>();
al1.add(5);
al1.add(6);
al1.add(7);
printColl(al1);
printColl(al);
}
//通配符?的应用,不明确类型,什么类型都可以传
public static void printColl(ArrayList<?> t){
Iterator<?> it=t.iterator();
while (it.hasNext()){
System.out.println(it.next().toString());
}
}*/
六、 泛型的限定
1. 定义: 对于一个范围内的一类事物,可以通过泛型限定的方式定义.
A. ? extends E:可接收E类型或E类型的子类型;称之为上限。
Vector<? extends Number> x = newvector<Integer>();
B. ? super E: 可接收E类型或E类型的父类型;称之为下限。
Vector<? super Integer>x = newvector<Number>();
/*高级应用:
泛型限定:1,通配符应用于将迭代器封装为函数
2,类的继承关系,函数上的泛型限定:
? extends E:可以接受E类型或者E的子类型 ,上限
? super E:可以接受E类型或者E的父类型, 下限
*/
import java.util.*;
class GenericDemo{
public static void main(String[] args){
ArrayList<person> al=new ArrayList<person>();
al.add(new person("秀and云1"));
al.add(new person("秀and云6"));
al.add(new person("秀and云3"));
ArrayList<student> al1=new ArrayList<student>();
al1.add(new student("长生----5"));
al1.add(new student("长生----6"));
al1.add(new student("长生----7"));
printColl(al1);
printColl(al);
}
//? extends E:可以接收E类型或者E的子类型。上限
public static void printColl(ArrayList<? extends person> al){
Iterator<? extends person> it=al.iterator();
while (it.hasNext()){
System.out.println(it.next().getName());
}
}
}
class person{
private String name;
person(String name){
this.name=name;
}
public String getName(){
return name;
}
}
class student extends person{
student(String name){
super (name);
}
}
八、 泛型类型的推断
1. 编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
2. 根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
a、当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) --> static <E> void swap(E[] a, int i, int j)
b 、 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) --> static <T> T add(T a, T b)
c 、 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) --> static <T> void fill(T[] a, T v) //编译通过
d、 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) --> static <T> T add(T a, T b) //编译通过
e、 参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) à static <T> void copy(T[] a,T[] b); //编译通过
copy(new Vector<String>(), new Integer[5]) à static <T> void copy(Collection<T> a , T[] b); //编译失败
九、通过反射获取泛型中的实际参数类型
public static void main(String[] args) throws Exception {
//Vector<String> v=new vector<String>();
//通过反射操作,获取集合中泛型的参数类型
Method m=GenricTest.class.getMethod("applyVector",Vector.class);
Type[] types=m.getGenericParameterTypes();
ParameterizedType pType=(ParameterizedType)types[0];
System.out.println(pType.getRawType());
System.out.println(pType.getActualTypeArguments()[0]);
}
//定义一个方法,并且接收一个带泛型的集合
public static void applyVector(Vector<String> v)
{
}
打印结果:
class java.util.Vector
class java.lang.String
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------