最近看Spring 源代码的时候遇到了一个关于泛型的问题,于是就请教我的老大帮我

解释了一下,然后又自己看了一点资料,感觉对泛型已经有了一定的理解了,就先

把这些想法记录下来,供有需要的人参考同时也为了能够加深理解

如果你在看JDK1.5的源码的时候就会发现,java.util.HashMap这个类里面出现了

这种代码:
public class HashMap<K,V>
    
extends AbstractMap<K,V>
    
implements Map<K,V>, Cloneable, Serializable
{



public HashMap(Map<? extends K, ? extends V> m) {.}
}

里面出现了大量的角号和K,V,T...等不明所以的代码,其实这就是泛型的应用。

泛型的一些概念:
List<E>  , List< String >   , List
List<E>是参数话类型 parameterized type ,
E是(formal)类型参数 type parameter,
String 实际类型参数 actual type argument,
List是自然类型 raw type

为什么会出现泛型:
原来的JDK中
当我不知道user到底是个什么对象的时候只能这样写:
static Object maskNull(Object o){
       return key == null ? NULL_KEY :key;
}

如果我在外面使用的时候:
User u = ...;
User user = (User)map.maskNull(u);
这里用到了向上转型,把map.maskNull(u)的返回对象强制转型为了User,强制转为什么类型完全有程序员控制,所以我在这里这样写也是可以的
Department d  =  (Department)map.maskNull(u);
这个错误在编译的时候是不会有任何错误提示的,必须到了runtime才能知道返回的类型和Department是不匹配的

但是如果使用了泛型以后我可以写成这样
static <T> T maskNull(T key) {
        return key == null ? (T)NULL_KEY : key;
    }

User u = ...;
User user = map.maskNull(u);

当我再写成Department d = map.maskNull(u);
的时候就会编译失败,因为方法要求参数和返回值必须是 T 类型的,当然这里的T只是暂时的一个声明,类似于去占一个坑,当我需要是User类型的时候,我就用 User去替换掉它,这样编译的时候就要求传进的和返回的必须是一致的类型,所以我们刚才的那种写法就不行了,这样编译器就使用自身的机制保证类型安全type-safe

但是泛型还存在另外一个重要的概念bridge method
先讲一下JDK对泛型的支持:上面的讲解虽然说泛型很有用,但是泛型在runtime是没有任何作用的,只是为了编译期的方便,所以当过了编译期之后还是要转换为普通代码的执行方式的,也就是说过了编译期,带角号的部分会被抹掉,变成了原始的方式
static Object maskNull(Object o){...}
那么现在看下面的代码:

class A<T>{
    T setX(T t){...}
}

class B extends A<String>{
    String setX(String s){...}
}

很明显,当B的方法void String setX(String s)重载了A的方法,但是按照我们刚才讲的理论A被编译成了
class A{
    Object setX(Object t){...}
}
也就是说经过编译期之后类B的方法就不重载了A的方法,这是不被允许的,所以载编译的时候同样有两外一件事情发生:JDK会帮你添加一个帮助方法来重载A的方法,于是B就变成了:
class B extends A{
    String setX(String s){...}
    Object setX(Object o){
       return setX((String)o);
    }
}

大家看到多了个重载A类方法的一个方法:
Object setX(Object o){...}
这个方法就是所谓的bredge method
这个工作是在编译期完成的。所以如果你遍历A上的方法的时候如果发现多了方法千万不要感觉以外

注意这个方法的重载对EL表达式和TPL的运行不存在影响,因为不管用的哪个方法,经过TPL的转型之后结果都是一样的