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, 代码如下:
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);
}
}
精华就是这句了:
泛型之后,所有BaseHibernateEntityDao的子类只要定义了泛型,就无需再重载getEnttityClass(),get()函数和find()函数,销益挺明显的,所以 SpringSide的Dao基类毫不犹豫就泛型了。
不过擦拭法的大棒仍在,所以子类的泛型语法可不能乱写,最正确的用法只有:
一般使用反射来获取泛型类型信息
如下代码:- package myGenetic;
- import java.lang.reflect.Field;
- import java.lang.reflect.ParameterizedType;
- import java.lang.reflect.Type;
- import java.util.Map;
- public class GenericTest {
- /*使用反射来获取泛型信息*/
- private Map<String, Integer> score;
- public static void main(String[] args) throws SecurityException, NoSuchFieldException {
- //Class clazz = GenericTest.class;
- Class<GenericTest> clazz = GenericTest.class;
- //System.out.println(clazz);
- Field f = clazz.getDeclaredField("score");
- //直接使用getType只对普通类型有效,无法取到泛型参数
- Class<?> a = f.getType();
- System.out.println("score的类型是:"+a);
- //获得Field实例f的泛型类型
- Type gType = f.getGenericType();
- //如果gType类型是ParameterizedType的对象
- if (gType instanceof ParameterizedType) {
- ParameterizedType pType = (ParameterizedType) gType;
- //获取原始类型
- Type rType = pType.getRawType();
- System.out.println("原始类型是:"+rType);
- //取得泛型类型参数
- Type[] tArgs = pType.getActualTypeArguments();
- System.out.println("泛型类型是:");
- for (int i = 0; i < tArgs.length; i++) {
- System.out.println("第"+i+"个泛型类型是:"+tArgs[i]);
- }
- }else{
- System.out.println("获取泛型类型出错!");
- }
- }
- }
输出结果如下:
score的类型是:interface java.util.Map
原始类型是:interface java.util.Map
泛型类型是:
第0个泛型类型是:class java.lang.String
第1个泛型类型是:class java.lang.Integer
还有一个较好的例子,多用于实现BaseDAO,代码如下:
- package myGenetic;
- import java.lang.reflect.ParameterizedType;
- class SubClass extends SuperClass<Person> {
- public SubClass() {
- super();
- }
- }
- class Person {
- public Person() {
- super();
- }
- public void function() {
- System.out.println("function in Person.class...");
- }
- }
- public abstract class SuperClass<T> {
- private Class<T> clazz;
- @SuppressWarnings("unchecked")
- public SuperClass() {
- clazz = (Class<T>) ((ParameterizedType) super.getClass()
- .getGenericSuperclass()).getActualTypeArguments()[0];
- }
- public Class<T> getClazz() {
- return clazz;
- }
- public void setClazz(Class<T> clazz) {
- this.clazz = clazz;
- }
- /**
- * 普通的非泛型类Class
- * 泛型化的类Class<T>
- * JDK中,普通的Class.newInstance()方法的定义返回Object,要将该返回类型强制转换为另一种类型;
- * 但是使用泛型的Class<T>,Class.newInstance()方法具有一个特定的返回类型;
- * @param args
- */
- public static void main(String[] args) {
- SuperClass<Person> subClass = new SubClass();
- //1.得到泛型类T实际的完整类名
- System.out.println(subClass.getClazz());
- //2.得到泛型类T的对象
- try {
- System.out.println(subClass.getClazz().newInstance());
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- //3.调用泛型类T的方法
- try {
- subClass.getClazz().newInstance().function();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
输出结果如下:
class myGenetic.Person
myGenetic.Person@10d448
function in Person.class..