泛型
泛型是提供给java编译器使用的,让编译器挡住程序的非法输入,对于参数化的泛型,getClass返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的信息,只要跳过编译器,就可以往泛型集合中加入其它类型的数据,如:用反射得到集合,再调用其add方法即可
泛型的由来:
1,泛型的出现将运行时期的问题ClassCastException 转移到了编译时期变成了编译失败。
2,泛型的出现避免了类型强制转换的麻烦。
3,泛型技术是用在编译时期的技术。
4,泛型的出现提高了类型的安全性。
泛型特点:
参数化类型与原始类型的兼容性
1.参数化类型引用一个原始类型的对象,编译报告警告
2.原始类型可以引用一个参数化类型的对象,编译报告警告
参数化类型不考虑类型参数的继承关系。
在创建数据实例时,数组的元素不能使用参数化的类型。
泛型的?号通配符:限定了可以接受的参数类型,此时,有些类型的方法不可以使用,比如add方法。
泛型通配符的扩展:限定了通配符的上边界和下边界,指定通配符总是包括自己。
Vector<? extends Number> x = new Vector<Integer>();
Vector<? super Integer> x = new Vector<Number>();
泛型综合实例:
遍历时将Map变为Set,set实现了Iterable接口,详见代码泛型综合实例
泛型类型推断:
1.根据传递的参数类型或返回值进行判断。
2.如果参数中、返回值多处使用了同一种类型,那么确定为这种类型。
3.参数和返回值中出现多个类型,且无返回值,则去类型的最大公约数。
4.参数和返回值出现多个类型,方法调用中使用了多中类型,优先考虑返回值的类型。
练习代码:
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/*
泛型是为了限定集合中存储对象的类型
*/
public class GenericTest {
public static void main(String[] args) throws Exception {
//创建一个String类型ArrayList
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("xyz");
//创建一个Integer类型的集合
ArrayList<Integer> al2 = new ArrayList<Integer>();
al2.add(1);
//虽然创建了两个不同类型的集合,但通过编译后,会去掉类型信息,所以他们的字节码是相同的
System.out.println(al.getClass() == al2.getClass());
//通过反射模拟add方法
//虽然al2是一个Integer类型的集合,但还是打印出了String类型的值
al2.getClass().getMethod("add",Object.class).invoke(al2, "abc");
System.out.println("al2.get(1):"+al2.get(1));
//演示:泛型中的通配符
printCollection(al2);
/*************泛型综合实例********************/
//定义一个Map
Map<String, Integer> maps = new HashMap<String, Integer>();
maps.put("xcq", 23);
maps.put("lxx", 23);
//想要遍历出Map中的元素,需要先将Map转换成Set
Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();
for(Map.Entry<String, Integer> en : entrySet){
System.out.println("entrySet||"+en.getKey()+":"+en.getValue());
}
//实例:自定义泛型
add(3,5);//T为int类型
add("abc",3);//T为Object类型
add(2.3, 3);//T为浮点类型
//实例:交换数组中两个值的位置
String [] str = new String[]{"abc","xyz","sss"};
swap(str,0,1);
for(int i=0; i<str.length; i++){
System.out.println("array.str:"+str[i]);
}
//需注意,当数组为int类型时程序不能完成自动装箱,不能将int类型转换成Integer对象,
//泛型的参数只能是引用类型,不能是基本数据类型
/***************反射获得泛型的实际类型参数******************/
//要获得泛型的实际类型,通过getClass()不能得到,可以通过Method方法中的属性获得
//通过反射中的method方法获得applyMethod方法
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
//获得applyMethod方法的类型
Type types[] = applyMethod.getGenericParameterTypes();
//通过方法的类型,获得参数类型
ParameterizedType pType = (ParameterizedType) types[0];
System.out.println("type1:"+pType.getRawType());
//打印参数类型为Date
System.out.println("type2:"+pType.getActualTypeArguments()[0]);
}
//applyMethod方法
public static void applyVector(Vector<Date> t){
}
//交换两个任意类型数组中两个位置的值
private static <T> void swap(T [] t, int i, int j) {
T tmp = t[i];
t[i] = t[j];
t[j] = tmp;
}
//方法中参数配置为?,可以接受任意类型的对象进来。
public static void printCollection(Collection<?> collection){
// collection.add(2);//此处add方法会接收一个含有类型的参数,所以当合参数不匹配时容易报错。
System.out.println("size:"+collection.size());//size方法中不含有类型相关的东西,可以使用,不用考
虑类型
//循环遍历集合中的内容
for(Object obj : collection){
System.out.println("obj:"+obj);
}
}
/****************实例:自定义泛型**********************/
public static <T> T add(T x, T y){
return null;
}
}
Dao代码:
/*
* 向数据库中添加对象时,想对某一个对象进行统一操作,比如定义crud方法进行操作时,可以确定操作的对象或返回的
* 是同一个对象,可以在类后添加对象的标识。
*/
public class GenerateDao<E> {
public void add(E x){
}
public E findById(int id){
return null;
}
public void delete(E obj){
}
public void update(E obj){
}
public Set<E> findByConditions(String where){
return null;
}
}