Java5泛型的用法,T.class的获取和为擦拭法站台
作者:
江南白衣
Java 5的泛型语法已经有太多书讲了,这里不再打字贴书。GP一定有用,不然Java和C#不会约好了似的同时开始支持GP。但大家也清楚,GP和Ruby式的动态OO语言属于不同的意识形态,如果是一人一票,我想大部分的平民程序员更热衷动态OO语言的平白自然。但如果不准备跳槽到支持JSR223的动态语言,那还是看看GP吧。
胡乱总结泛型的四点作用: 第一是泛化,可以拿个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里面还没有tyepdef.....),因此对范型十分不感冒,这时就要齐齐感谢这个搽拭法让你依然可以对一个泛型框架保持非泛型的用法了...
通过反射获得 T.class:
不知为何书上不怎么讲这个,是差沙告诉我才知道的,最经典的应用见Hibernate wiki的
Generic Data Access Objects, 代码如下:
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);
}
}
精华就是这句了:
Class
<
T
>
entityClass
=
(Class
<
T
>
) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[
0
];
泛型之后,所有BaseHibernateEntityDao的子类只要定义了泛型,就无需再重载getEnttityClass(),get()函数和find()函数,销益挺明显的,所以
SpringSide的Dao基类毫不犹豫就泛型了。
不过擦拭法的大棒仍在,所以子类的泛型语法可不能乱写,最正确的用法只有:
public
class
BookDao
extends
BaseHibernateEntityDao
<
Book
>
评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
package test;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import junit.framework.TestCase;
class TClass<T> {
}
class GoodClass<T> extends TClass<String> {
public ParameterizedType getClassT() {
return (ParameterizedType) getClass().getGenericSuperclass();
}
}
class BadClass<T> extends TClass<T> {
public ParameterizedType getClassT() {
return (ParameterizedType) getClass().getGenericSuperclass();
}
}
public class GenericsTest extends TestCase {
private void print(Type[] targs) {
System.out.print("actual type arguments are:");
for (int j = 0; j < targs.length; j++) {
System.out.print(" instance of " + targs[j].getClass().getName() + ":");
System.out.println(" (" + targs[j] + ")");
}
}
public void testGoodClass() throws Exception {
ParameterizedType type = new GoodClass<String>().getClassT();
Type[] types = type.getActualTypeArguments();
print(types);
assertEquals(TClass.class, type.getRawType());
assertEquals(String.class, types[0]);
}
public void testBadClass() throws Exception {
ParameterizedType type = new BadClass<String>().getClassT();
Type[] types = type.getActualTypeArguments();
print(types);
assertEquals(TClass.class, type.getRawType());
assertEquals(String.class, types[0]);
}
}
faint 评论于 2006-04-28 21:52 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
你用的smartdraw和jude都是什么版本的?
anonymous 评论于 2006-04-30 10:20 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
faint里面所举例的那个程序很经典呀。
刚才试验了一下Generic无法获取自己的Generic Type类型,后来仔细看了下擦拭法的本意。实际上BadClass<String>()实例化以后Class里面就不包括T的信息了,对于Class而言T已经被擦拭为Object,而真正的T参数被转到使用T的方法(或者变量声明或者其它使用T的地方)里面(如果没有那就没有存根),所以无法反射到T的具体类别,也就无法得到T.class。而getGenericSuperclass()是Generic继承的特例,对于这种情况子类会保存父类的Generic参数类型,返回一个ParameterizedType,这时可以获取到父类的T.class了,这也正是子类确定应该继承什么T的方法。
Tin 评论于 2006-05-08 21:21 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
一直觉得泛型是花哨东西,正如老兄所说Java的Collection库没有范型一样过得好好的。BaseHibernateEntityDao改过后没感觉有多大方便,还是觉得以前的写法好,也许是习惯问题吧。
简单 评论于 2006-05-26 06:18 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
baseDao用后泛型后, 子类可以不用写任何函数也有基本的crud功能啊
之前要重载getEntityClass函数,而且find单体的函数如get(id) 也要重载做类型转换.
江南白衣 评论于 2006-05-26 12:05 回复 更多评论
# 跑题一下
IUSR 评论于 2006-06-08 01:22 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
<Dont' repeat DAO> 在
http://www.nirvanastudio.org/上有很好的中译版。
http://www.nirvanastudio.org/java/dont-repeat-the-dao.html
里面那介绍的方式虽然真正实用性不高,但是很好的用Interface+AOP实现Java版的仿Ruby的mixin的思路演示
江南白衣 评论于 2006-06-09 00:01 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台[未登录]
终于知道了泛型的好处,以前我也是困扰用反射找不到class,多谢白衣!强顶!
Michael 评论于 2007-02-28 10:21 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
good
bearhunter 评论于 2007-05-25 19:40 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
谢谢白衣,学到很多.
大众 评论于 2007-09-29 09:41 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
http://www.javazh.cn
java综合网
不错,不错~~~
java综合网 评论于 2008-01-14 15:33 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
平常泛型是用了不少,今天才领悟到真谛!顶一个
R_XiaoGuang 评论于 2008-07-20 08:18 回复 更多评论
# re: Java5泛型的用法,T.class的获取和为擦拭法站台
多谢白衣,正当我被这个T搞得头晕时,找到了这里,而且例子正是我想要的,呵呵,多谢了!