Java泛型的⽤法及T.class的获取过程解析
这篇⽂章主要介绍了Java泛型的⽤法及T.class的获取过程解析,⽂中通过⽰例代码介绍的⾮常详细,对⼤家的学习或者⼯作具有⼀定的参考学习价值,需要的朋友可以参考下
胡乱总结泛型的四点作⽤:
第⼀是泛化,可以拿个T代表任意类型。但GP是被C++严苛的静态性逼出来的,落到Java、C#这样的花语平原⾥----所有对象除⼏个原始类型外都派⽣于Object,再加上Java的反射功能,Java的Collection库没有范型⼀样过得好好的。
第⼆是泛型 + 反射,原本因为Java的泛型拿不到T.class⽽觉得泛型没⽤,最近才刚刚学到通过反射的API来获取T的Class,后述。
第三是收敛,就是增加了类型安全,减少了强制类型转换的代码。这点倒是Java Collection历来的弱项。
第四是可以在编译期搞很多东西,⽐如MetaProgramming。但除⾮能完全封闭于框架内部,框架的使⽤者和扩展者都不⽤学习这些东西的⽤法,否则那就是⾃绝于⼈民的票房毒药。C++的MetaProgramming好厉害吧,但对⽐⼀下Python拿Meta Programming⽣造⼀个Class出来的简便语法,就明⽩什么才是真正的叫好⼜叫座。
所以,作为⼀个架构设计师,应该使⽤上述的第2,3项⽤法,在框架类⾥配合使⽤反射和泛型,使得框架的能⼒更强;同时采⽤收敛特性,本着对⼈民负责的精神,⽤泛型使框架更加类型安全,更少强制类型转换。
擦拭法避免了Java的流⾎分裂:
⼤家经常骂Java GP的擦拭法实现,但我觉得多亏于它的中庸特性---如果你⽤就是范型,不⽤就是普通Object,避免了Java阵营⼜要经历⼀场to be or not to be的分裂。
最⼤的例⼦莫过Java 5的Collection 框架,⽐如有些同学坚持认为⾃⼰不会⽩痴到类型出错,⽽且难以忍受每个定义的地⽅都要带⼀个泛型定义List〈Book〉,不⽤强制类型转换所省下的代码还不够N处定义花的(对了,java⾥⾯还没有),因此对范型⼗分不感冒,这时就要齐齐感谢这个搽拭法让你依然可以对⼀个泛型框架保持⾮泛型的⽤法了...
<<⼲货来了>>
通过反射获得 T.class:
abstract public class BaseHibernateEntityDao<T> extends HibernateDaoSupport {
private Class<T> entityClass;
public BaseHibernateEntityDao() {
entityClass =(Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
} public T get(Serializable id) { T o = (T) getHibernateTemplate().get(entityClass, id); return o; } }
重点就是这句话:java反射获取父类属性
Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
解释:
1. public Type getGenericSuperclass()
⽤来返回表⽰当前Class 所表⽰的实体(类、接⼝、基本类型或 void)的直接超类的Type。如果这个直接超类是参数化类型的,则返回的Type对象必须明确反映在源代码中声明时使⽤的类型。⽐如:
import flect.ParameterizedType;
public class GT1 extends GT2<Integer>{
public static void main(String[] args) {
System.out.println(((ParameterizedType)new GT1().getClass().getGenericSuperclass()));
}
}
则输出结果即为:
GT2<java.lang.Integer>
如果此Class代表的是Object 类、接⼝、基本类型或 void,则返回 null。。如果此对象表⽰⼀个数组类,则返回表⽰ Object 类的 Class 对象。
2. public Type[] getGenericInterfaces()
与上⾯那个⽅法类似,只不过Java的类可以实现多个接⼝,所以返回的Type必须⽤数组来存储。
以上两个⽅法返回的都是Type对象或数组,在我们的这个话题中,Class都是代表的参数化类型,因此可以将Type对象Cast 成ParameterizedType对象。⽽ ParameterizedType对象有⼀个⽅法, getActualTypeArguments()。
public Type[] getActualTypeArguments()
⽤来返回⼀个Type对象数组,这个数组代表着这个Type声明中实际使⽤的类型。接着使⽤上⾯的例⼦:
import flect.ParameterizedType;
public class GT1 extends GT2<Integer>{
public static void main(String[] args) {
System.out.println(((ParameterizedType)new GT1().getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
}
}
这次的显⽰结果将是:class java.lang.Integer
因此,我们可以通过继承+反射的⽅法,来的到T.class。
需要说明的是,江南⽩⾐使⽤的⽅法是将关键语句
Class < T > entityClass = (Class < T > ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[ 0 ];
放在了超类,也就是声明泛型的那个类的构造⽅法中。这样⼀来,⼦类在继承具有泛型的超类时,会⾃动调⽤超类的构造⽅法。在此超类的构造⽅法中,调⽤的getClass返回的是⼦类的Class类型(与通常的重写机制有悖,呵呵,有待深究,但测试结果确是如此),则在⼦类中就⽆需再显式地使⽤getGenericInterfaces()和getGenericSuperclass()等⽅法了。
接着,再使⽤(Class<T>)对 getActualTypeArguments()返回的元素做casting,即可得到所谓的T.class。
泛型之后,所有BaseHibernateEntityDao的⼦类只要定义了泛型,就⽆需再重载getEnttityClass(),get()函数和find()函数,销益挺明显的,所以SpringSide的Dao基类毫不犹豫就泛型了。
不过擦拭法的⼤棒仍在,所以⼦类的泛型语法可不能乱写,最正确的⽤法只有:
public class BookDao extends BaseHibernateEntityDao<Book>
个⼈见解:即他们之间的关系就是 BaseHibernateEntityDao 是⼀个⽗类,他可以是单纯的⼀个类,也可以是实现接⼝的类,⽽应其中泛型的缘故,所以他直接实例化调⽤其中⽅法是没有意义的,因为泛型的类型⽆法确定,所以只有通过⽤其它类去继承它,在继承时将泛型传⼊,此时若实例化继承了BaseHibernateEntityDao 的类,BaseHibernate EntityDao的构造器中就会通过反射确定该泛型的类型.(有误请指正)
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。