----------------- android培训、java培训、期待与您交流!-----------------
泛型
泛型是jdk1.5的新特性之一,用于解决安全问题,是一个安全机制。泛型是提供给javac编译器使用的,可以限定集合中输入类型,让编译器拦住程序中的非法输入。编译器编译带泛型的集合时会去掉“类型”的信息,使程序运行效果不受影响,运行时是无泛型的。
泛型是jdk1.5的新特性之一:
1 泛型的出现,将运行时出现的错误放到了编译期
2 也避免了在以前存在对象的转型问题,泛型的出现可以将集合中的元素限定为一个特定的类型
ArrayList al = new ArrayList();
al.add("abc");
// al.add(new persion("zhangsan",20));
// al.add(123); //这两行代码编译时就报告了语法错误
String str = al.get(0); //不需要再进行类型转换
public static void array() throws Exception{
ArrayList al =new ArrayList(); //限定只能输入字符串
al.add("ab");
al.add("bc");
String str =al.get(0); //避免了转型
System.out.println(str);
ArrayList al2 =new ArrayList();
al2.add(3);
al2.add(10);
int x =al2.get(0);
System.out.println(x);
//编译器生成的字节码会去掉泛型的类型信息 ,所以打印的结果是true
System.out.println("al.class == al2.class is :" + (al.getClass() ==al2.getClass()));
//反射方式,由于编译器生成的字节码会去掉泛型的类型信息,
//所以用反射可跳过编译器,存入任何类型
al.getClass().getMethod("add", Object.class).invoke(al, 4);
System.out.println(al);
}
整个Set
整个Set
在Set
Set称为原始类型
参数化类型与原始类型的兼容性:
1 参数化类型可以引用一个原始类型的对象
Set
2 原始类型可以引用一个参数化类型的对象
Set set = new Set
注意:
1 参数化类型不考虑类型参数的继承关系
ef: Set
2 创建数组实例时,数组的元素不能使用参数化类型。因为编译器不允许创建泛型变量的数组
ef: ArrayList
ArrayList [] aa =new ArrayList [10]; 正确
现在来看看 泛型中 ? 通配符
Collection> a可以与任意参数化的类型匹配,但到底匹配的是什么类型,只有以后才知道。所以,a=new ArrayList
和a=new ArrayList
public static void printCollection(Collection> coll) {
for(Object obj:coll) {
System.out.println(obj);
}
//coll.add("string"); //错误,因为它不知自己未来匹配就一定是String
coll.size(); //正确,此方法与类型参数没有关系
coll = new HashSet();
}
总结:
使用?通配符可以引用其他各种参数化的类型,最主要作用是引用。
可以调用与参数化无关的方法,不能调用与参数化有关的方法
通配符的上下边界:
上边界(向上限定):
? extends Number : 表示Number类或继承自Number类
ArrayList extends Number> all =new ArrayList
ArrayList extends Number> al2 =new ArrayList
下边界(向下限定):
? super Integer : 表示Integer类或Integer的父类
ArrayList super Integer> all =new ArrayList
ArrayList super Integer> all =new ArrayList
类型参数的类型判断:
编译器判断范型方法的实际类型参数的过程称为类型推断,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1 当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,
这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
printStr(new String[3],3,4) public static
如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) public static
如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,
例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) publicstatic
如果调用方法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,这时候优先考虑返回值的类型,
例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,
接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) public static
编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) publicstatic
copy(new Vector
自定义泛型:
方法级别泛型:
普通方法、构造方法和静态方法中都可以使用泛型。
public
public static
在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:
public static
类级别泛型:
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方 式进行定义,也就是类级别的泛型,也能定义在接口上。语法格式如下:
public class Test
private T
public void save(T obj){}
public T getById(int id){}
}
注意:
1 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
2 当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。
因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
/**
* 泛型不仅能在类上使用还能在方法中使用
* 在方法中使用的好处---
* 1 是避免了在类上使用泛型时,
* 一旦定义确定了类型就不能传递其他类型参数的问题
*2 可以使用方法操作不同类型,
*/
class a2{ //class a2可以同时在类和方法上使用泛型
public void print(qq q){
System.out.println("print:"+q);
}
public void show(gg g){
System.out.println("show:"+g);
}
/*public static void func(T t){ --错误的泛型使用
特殊点: 静态方法是不能使用类上定义的泛型--->因为静态方法在对象创建前
如果静态方法要使用泛型--可以在方法上定义:
public static void func(qq q){
system.out.println("static func :" + q);
}
}*/
}
通过反射获得泛型的参数化类型:
public class GenericTest
{
public static void main(String[] args)
{
//获取public static void Test(Set set){}方法
Method testMethod = GenericTest.class.getMethod("Test", Set.class);
//获取方法的参数列表
Type type[] = testMethod.getGenericParameterTypes();
ParameterizedType ptype= (ParameterizedType)type[0];
//打印集合类型
System.out.println(ptype.getRawType());
//获取集合存储对象
System.out.println(ptype.getActualTypeArguments()[0]);
}
public static void Test(Set set){
}
}