正文前先来一波福利推荐:html
福利一:java
百万年薪架构师视频,该视频能够学到不少东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女友公众号也方便你们学习,共享给你们。sql
福利二:数据库
毕业答辩以及工做上各类答辩,平时积累了很多精品PPT,如今共享给你们,大大小小加起来有几千套,总有适合你的一款,不少是网上是下载不到。数组
获取方式:微信
微信关注 精品3分钟 ,id为 jingpin3mins,关注后回复 百万年薪架构师 ,精品收藏PPT 获取云盘连接,谢谢你们支持!架构
-----------------------正文开始---------------------------框架
一、背景介绍
在实现SSH框架中,DAO层向数据库持久化的过程当中,由于大部分保存对象的方法都会调用到sava();全部索性就把save delete update select 方法进行封装到父类中,这时候就遇到了个问题,子类在调用这些方法的时候,须要根据子类的类型获知子类Class类型;这个时候能够经过传入泛型,根据泛型的类型来获取子类的Class类型; 函数
二、实现代码范例
父类:public abstract class Parents<E>学习
{
private Class<?> child;
public Parents() { Class<?> c = this.getClass(); //子类建立 会建立父类 子类调用时 此处的this是子类 Type t = c.getGenericSuperclass(); //得到带有泛型的父类 if (t instanceof ParameterizedType) { Type[] p = ((ParameterizedType) t).getActualTypeArguments(); //取得全部泛型 this.child= (Class<E>) p[0]; }
}
在子类调用父类的方法时,若是父类的方法中须要知道具体子类的Class类型 则能够直接使用Child来使用;
此处的原理就是在子类继承父类的时候 带有泛型 而后子类在建立的时候,会调用父类的构造函数,构造函数中存在this指的的是子类,而后经过得到父类,再得到父类的泛型
;经过泛型找到子类类型;
三、原理分析
该实现是经过反射技术实现;下面看具体的分析;
三、1 ParameterizedType 类
ParameterizedType,参数化类型,形如:Object<T, K>,即常说的泛型,是Type的子接口。
public interface ParameterizedType extends Type { //1.得到<>中实际类型 Type[] getActualTypeArguments(); //2.得到<>前面实际类型 Type getRawType(); //3.若是这个类型是某个类型所属,得到这个全部者类型,不然返回null Type getOwnerType(); }
1.getActualTypeArguments
得到参数化类型中<>里的类型参数的类型,由于可能有多个类型参数,例如Map<K, V>,因此返回的是一个Type[]数组。
注意:不管<>中有几层<>嵌套,这个方法仅仅脱去最外层的<>,以后剩下的内容就做为这个方法的返回值,因此其返回值类型不必定。
例如:
1. List<ArrayList> a1;//这里返回的是,ArrayList,Class类型 2. List<ArrayList<String>> a2;//这里返回的是ArrayList<String>,ParameterizedType类型 能够继续经过调用getActualTypeArguments得到其泛型类型 3. List<T> a3;//返回的是T,TypeVariable类型 4. List<? extends Number> a4; //返回的是WildcardType类型 5. List<ArrayList<String>[]> a5;//GenericArrayType 要注意,ArrayList与ArrayList<String>的不一样。
public static void main(String[] args) throws Exception { Method method = new Main().getClass().getMethod("test", List.class);//这里的第二个参数,和getRawType()意义相似 Type[] types = method.getGenericParameterTypes(); ParameterizedType pType = (ParameterizedType) types[0]; Type type = pType.getActualTypeArguments()[0]; System.out.println(type); //type是Type类型,但直接输出的不是具体Type的五种子类型,而是这五种子类型以及WildcardType具体表现形式 System.out.println(type.getClass().getName()); } public void test(List<ArrayList<String>[]> a)
{ }
2.getRawType
返回最外层<>前面那个类型,即Map<K ,V>的Map
Map<Integer, String> maps = new HashMap<>(); ParameterizedType pType = (ParameterizedType) maps.getClass().getGenericSuperclass();//得到HashMap的父类 System.out.println(pType.getRawType());//class java.util.AbstractMap if(pType.getRawType() instanceof Class){ System.out.println("true");//true } //注意类型(Type)与类(Class)的区别
三、2 Type类
Type是java类型信息体系中的顶级接口,其中Class就是Type的一个直接实现类。此外,Type还有有四个直接子接口:ParameterizedType,TypeVariable,WildcardType,GenericArrayType。
引用这位仁兄对这几个接口的介绍[转载]:
Type
它是全部类型的公共接口。包括原始类型、参数化类型、数组类型、类型变量和基本类型。ParameterizedType, TypeVariable, WildcardType,GenericArrayType这四个接口都是它的子接口。
三、二、1 GenericDeclaration
这个接口Class、Method、Constructor都有实现,咱们就是要用这个接口的getTypeParameters方法,它返回一个TypeVariable[]数组,这个数组里面就是咱们定义的类型变量T和K,顺序与咱们声明时同样。若是用循环语句将数组打印出来,你会发现只会输出T和K,这可不是咱们想要的结果,那么想要得到预期的结果怎么办呢?请继续往下看。
三、二、2 TypeVariable
它表示类型变量。好比T,好比K extends Comparable<? super T> & Serializable,这个接口里面有个getBounds()方法,它用来得到类型变量上限的Type数组,若是没有定义上限,则默认设定上限为Object,请注意TypeVariable是接口,实际获得的是TypeVariableImpl实现类,下面几个接口都同样。
拿T和K来讲明,T没有定义任何上限,因此它就有一个默认上限java.lang.Object,实际跟踪代码的时候你会发现T的bounds属性为空,只有在调用了getBounds()方法后,才会有一个Type[1]数组[class java.lang.Object]。而对于K来讲,调用了getBounds方法后,获得的数组是[java.lang.Comparable<? super T>, interface java.io.Serializable],它们的类型倒是不同的,第1个是ParameterizedType,而第二个是Class
三、二、3 ParameterizedType
ParameterizedType表示参数化类型,就是上面说的java.lang.Comparable<? super T>,再好比List<T>,List<String>,这些都叫参数化类型。获得Comparable<? super T>以后,再调用getRawType()与getActualTypeArguments()两个方法,就能够获得声明此参数化类型的类(java.lang.Comparable)和实际的类型参数数组([? super T]),而这个? super T又是一个WildcardType类型。
三、二、4 WildcardType
它用来描述通配符表达式,上面返回的? super T正好是这个类型。而后调用getUpperBounds()上限和getLowerBounds()下限这两个方法,得到类型变量?的限定类型(上下限),对于本例的通配符(?),它的上限为java.lang.Object,下限为T
经过上面几个接口的分析,能够将Person类的泛型参数都解析出来,那么Person的超类以及实现的接口该怎么处理呢?Class类里面一样在1.5版本加入了getGenericSuperclass()与getGenericInterfaces()两个方法,用于返回带参数化类型的超类与接口。
三、二、5 GenericArrayType其实就是泛型数组类型。
咱们说Class在必定程度上挽救了擦除的类型信息,咱们就能够经过这几个接口来获取被擦除的类型参数信息,这几个接口无非就是对类型参数的一个分类,经过它们提供的一些方法,咱们能够逐步的获取到最原始的类型参数的Class对象。
具体的说明和API你们能够去看文档,我这里记录一个实际的应用,固然在各类框架中的应用比比皆是。
在JavaEE的Dao层咱们通常都会封装出一个通用的泛型BaseDao,它能够实现对各类实体例如User,Order的基本CRUD,而后具体的UserDao,OrderDao等等会去继承它,提供其余的Dao方法:
public class UserDao extends BaseDao<User>{} 我使用的BaseDao是基于DBUtils的,它须要实体的Class对象才能进行通用的查询方法,例如User的Class对象,咱们能够经过构造函数,函数参数等手段传递给BaseDao,可是有了反射,能够有更优雅的实现。 public class BaseDao<T> { private Class<T> clszz; public BaseDao(){ Type type = this.getClass().getGenericSuperclass();//拿到带类型参数的泛型父类 if(type instanceof ParameterizedType){//这个Type对象根据泛型声明,就有多是4中接口之一,若是它是BaseDao<User>这种形式 ParameterizedType parameterizedType = (ParameterizedType) type; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();//获取泛型的类型参数数组 if(actualTypeArguments != null && actualTypeArguments.length == 1){ if(actualTypeArguments[0] instanceof Class){//类型参数也有可能不是Class类型 this.clszz = (Class<T>) actualTypeArguments[0]; }else{ //例如: BaseDao<BaseDao<User>>,获取到的就不是Class,而又是ParameterizedType,即嵌套的 ParameterizedType,一层一层剥开,最终是能够获得User的Class对象的 } } } } public T get(String sql,Object...params){ QueryRunner qr = new QueryRunner(); T obj; Connection connection; try { connection = JdbcUtil.getConnection(); obj = qr.query(connection,sql,new BeanHandler<T>(clszz),params); } catch (SQLException e) { e.printStackTrace(); return null; } return obj; } }