### 为什么需要泛型
我们如果需要一个动物园(Zoo)然后里面会生活着很多小动物(animals),如果没有泛型的情况下去实现这样一个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  interface  Animal  {public  class  Cat  implements  Animal  {public  class  Dog  implements  Animal  {public  class  Zoo  {private  Animal[] animals;public  void  add (Animal e)  {public  Animal get (int  index)  {return  animals[index];
因为animals有很多种,小狗小猫猴子等,所以我们用Animal的数组,这样可以满足我们的需求,如下代码
1 2 3 4 Zoo  zoo  =  new  Zoo ();new  Cat ());new  Dog ());Animal  animal  =  zoo.get(0 );
然后我需要一个全是小狗的动物园
1 2 3 4 5 6 Zoo  zoo  =  new  Zoo ();new  Dog ());new  Dog ());new  Cat ());Dog  dog  =  (Dog) zoo.get(0 );Dog  dog  =  (Dog) zoo.get(2 );
当我们变换需求为全是小狗的动物园后,发现这里会有两个问题
1、这里是需要强制转换类型的;2、我们没办法去校验加入的类型是不是我们需要的类型,有可能是错误的,比如代码中的(1) ,会导致代码中(2) 的错误
如果要解决上面这个问题我们可以写一个叫做DogZoo,全是小狗的动物园
1 2 3 4 5 6 7 8 9 10 public  class  DogZoo  {private  Dog[] dogs;public  void  add (Dog e)  {public  Dog get (int  index)  {return  dogs[index];
但是这样就会写一堆的Zoo,比如CatZoo,MonkeyZoo等等。
所以就诞生的泛型,有了泛型之后我们就可以写出如下的代码
1 2 3 4 5 6 7 8 9 10 public  class  Zoo <T> {private  T[] animals;public  void  add (T e)  {public  T get (int  index)  {return  animals[index];
这样我就可以拥有我想拥有的动物园了:
1 2 3 4 5 6 7 8 9 10 11 new  Zoo <Animal>();new  Dog ());new  Cat ());Animal  animal  =  animalZoo.get(1 );new  Zoo <Dog>();Dog  dog  =  dogZoo.get(1 );new  Zoo <Cat>();Cat  cat  =  catZoo.get(1 );
泛型怎么用 泛型类 1 2 3 4 5 6 7 8 9 10 public  class  Zoo <T> {private  T[] animals;public  void  add (T e)  {public  T get (int  index)  {return  animals[index];
Zoo类其实就是一个带泛型的类,声明类的时候就需要给类一个类型,例如下面
1 2 Zoo<Dog> dogZoo = new  Zoo <Dog>();new  Dog ());
泛型接口 1 2 3 4 public  interface  Food <T> {public  void  eat (T food) ;
1 2 3 4 5 6 public  class  Cat  extends  Animal  implements  Food <String> {@Override public  void  eat (String food)  {
声明类使用接口泛型的时候需要把泛型类型一块定义好
泛型方法 1 2 3 4 public  <E> void  addE (E e) {
这里有个容易迷惑的地方,如下面所示
1 2 3 4 5 6 7 8 9 public  class  Zoo <T> {public  T get (int  index) {return  animals[index];public  <T> void  addT (T t) {
这里的addT方法的T和类Zoo上的T其实代表的不是一个类型,方法上的类型是优先级是高于类上面的泛型,在我们正常使用过程中,最好不要出现类泛型和方法泛型使用同样的字母表示。
泛型静态方法 如下例子
1 2 3 4 5 6 7 8 9 10 public  class  Zoo <T> {private  T[] animals;public  void  add (T e)  {public  static  T staticT (T t) {return  t;
这时代码行1是报错的,类中的静态方法使用泛型的话,要自己定义一个类型,如下面所示:
1 2 3 4 5 6 7 8 9 10 public  class  Zoo <T> {private  T[] animals;public  void  add (T e)  {public  static  <E> E staticE (E e) {return  e;
多个泛型类型 1 2 3 4 public  class  Zoo <T, E> {private  T[] animals;private  E[] Zookeepers;
协变,逆变是个啥? 协变,逆变,不型变,通俗理解 
**invariance(不型变)**:就是上述例子中的Zoo< Animal>和Zoo< Dog>没有关系
covariance(协变) : Zoo 是 Zoo 的子类型  
contravariance(逆变) :Zoo 是 Zoo 的子类型  
当然上面的说法在Java中是不准确的,在Java中Zoo和Zoo总是没有关系的,但是为了方便理	解先这么表示,正确的应该是Zoo 是 Zoo<?extend Animal> 的子类型,Zoo 是 Zoo<? super 	Dog> 的子类型(子类型和子类也不是一个概念)。    
Java中泛型为什么是不型变的 
假如Java的泛型不是不型变的,看下面的代码
1 2 3 Zoo<Dog> dogZoo = new  Zoo <Dog>(); new  Cat ()); 
在Java中代码行2是不被编译器允许的,假如他被允许,我们都知道在Java中代码行1中的dogZoo其实是指向new Zoo()对象的一个引用,同理在代码行2中animalZoo也相当于是一个指向了new Zoo()对象的引用,所以此时animalZoo按理说只能存Dog才对,如果代码行2被允许,那么代码行3也要被允许,但是这明显是不对的,因为此时animalZoo只能存Dog类型才对。所以如果代码行2允许的话,那么代码行3就不能被允许,也就是animalZoo的add方法就不能被允许,如果这样的话所有的泛型类都不被允许使用add方法了,那指定是不可以的。所以最理想的状态是有一个泛型类是没有add方法的,也就衍生出了协变。  
协变 
Java中的协变通过给泛型类加? extend来实现,这样编译器就会允许如代码行1中那样写了,为了避免上面的问题,同时禁用了add方法。这样可以保证不会出错,并且animalZoo可以代表此泛型类及其字类泛型。
1 2 3 4 Zoo<? extends  Animal > animalZoo = dogZoo;new  Cat ()); new  Dog ());Animal  animal  =  animalZoo.get(1 );
代码行2和代码行3都不行,即使代码行3在此处应该可以,但是对于animalZoo来说,此时它可以指向任何它本身以及其字类的泛型类。
其实说到这里大家虽然懂了这些,但不禁有个疑惑,上面的情况完全没有必要使用代码行1那样的操作,因为实际写代码的时候根本不会这么用,那就是这么做的意义是什么呢?看下面的两个需求:
1.需要获取不同类型动物园的符合某种动物特性的动物
没有使用? extend写法,有重复代码
1 2 3 4 5 6 for  (Dog dog: dogZoo.animals){for  (Animal animal: animalZoo.animals){
改进,使用? extend特性:
1 2 3 4 5 6 7 8 9 10 public  Animal getAnimal (Zoo<? extends Animal> zoo) {for  (Animal animal : zoo.animals){return  animal;return  null ;
方便
2.Animal动物园需要一群一群的添加小动物
没有? extend的写法:
1 2 3 public  void  addAll (Collection<T> animals)  {
这会带来一个问题,animalZoo不能添加dogZoo的小动物,也不能添加catZoo的小动物,明明是可以的,所以修改一下,改成下面:
1 2 3 public  void  addAll (Collection<? extend T> animals)  {
这样就可以实现上面的需求了。
逆变 
与协变相反的是逆变,在Java中使用? super来表示,即把泛型类型限定到了此类以及它的父类上,如下:
1 2 3 4 5 6 7 Zoo<? super  Animal> animalZoo1 = dogZoo; super  Animal> animalZoo1 = animalZoo; new  Dog ());new  Cat ());super  Dog> dogZoo1 = animalZoo;new  Dog ());new  Animal ());
从这个例子中我们可以发现被? super 修饰后只能指向本身及其父类,例如代码行2和代码行5,此时只能向里面添加super修饰符修饰的类型,因为此时指向的是本身的泛型类或者父类的泛型类,对于代码行7,在此时 按理说应该是正确的,因为dogZoo1指向了Animal类型的对象,但是对于dogZoo1来说也有可能指向Dog类型的对象,所以编译器就做了一个限制,只能添加super后面类型的对象,对于然后?super修饰的类型指向范围被限定为本身及其父类。
那么同样对于? super的使用又有哪些地方用到呢,
我想对两条条小狗做一些每个小狗入园都要做的操作(比如洗澡打针)后再加入不同的动物园
不优雅的做法
1 2 3 4 5 6 Dog  dog1  =  new  Dog ();Dog  dog2  =  new  Dog ();
这里就出现了重复代码就是省略的那部分,然后修改一下
1 2 3 4 5 6 addDog(dogZoo,dog1);public  void  addDog (Zoo<?super  Dog> canAdddogZoo,Dog dog) {
是不是好了很多。
协变逆变总结 
对于? extend 和 ? super的使用,我们只需要记住当需要返回T的时候就用?extend,是一个生产者;当需要写入一个T的时候使用?super,他是一个消费者。
这一部分是比较难以理解的,主要是不清楚为什么和怎么用,我也是搞了很久才搞明白,查阅了好多文章,总感觉少了点什么,所以我也就把我自己的理解写了下,有些表达应该是欠严谨,发现问题的朋友可以给我留言,我们一块探讨。
泛型在Java中的实现(类型擦除) Java是通过类型擦除来实现泛型的:也就是说我们写出来的泛型代码跟虚拟机去执行的代码并不是一样的代码
比如我们上面的例子中:
1 2 3 4 5 6 7 8 9 10 public  class  Zoo <T> {private  T[] animals;public  void  add (T e)  {public  T get (int  index)  {return  animals[index];
在要执行的虚拟机的眼里是这个样子的:
1 2 3 4 5 6 7 8 9 10 public  class  Zoo  {private  Object[] animals;public  void  add (Object e)  {public  Object get (int  index)  {return  animals[index];
再比如我们写的代码是这个样子的:
1 2 3 Zoo<Dog> dogZoo = new  Zoo <Dog>();Dog  dog  =  dogZoo.get(1 );
虚拟机里看到是这个样子的:
1 2 3 Zoo  dogZoo  =  new  Zoo ();Dog  dog  =  (Dog) dogZoo.get(1 );
带来的问题 
泛型T不可以使用基本数据类型如int,double等,因为擦除后是Object嘛
无法获得泛型的Class
不能实例化泛型,例如T(),但是可以通过反射来实例化T的类型,如下
1 2 3 public  Zoo (Class<T> tClass) {
 
Kotlin中的泛型有啥不同 Kotlin与Java在实现泛型的底层原理是一致的,毕竟Kotlin也是要转换成Java的,但是Kotlin在Java的基础上做了一些比较方便的改变:
替换? extend为out ? super为in
Java对型变的处理都放到了使用处,导致有一些类只是生产者也需要在使用处多次定义
还是上面那个动物园的例子,此时的动物园只有get方法,没有add方法,说明只是一个生产者
1 2 3 4 5 6 7 8 public  class  ZooProducer <T> {private  T[] animals;public  T get (int  index)  {return  animals[index];new  ZooProducer <Dog>();
此时代码行1是报错的,这不意外,虽然这不太合适,毕竟我只是生产者,但是编译器不知道,但是这个问题在Kotlin中得到了解决,Kotlin可以在类声明处进行限制,如下代码
1 2 3 4 5 6 7 8 class  ZooProducer1 <out T > {private  val  animals: Array<out  T> = TODO()fun  get () return  animals[1 ]var  animalsZooProducer: ZooProducer1<Animal1> = ZooProducer1<Dog>()
这就带来了极大的便利,对于? super和in也是同理
当一个类不能唯一确定一定是生产者或者一定是消费者的时候,和Java一样,在使用处型变就可以。其他的好像就没啥不一样的,总体而言,Kotlin的泛型是更简洁,灵活而且严格的,这也是Kotlin的特点。
这篇博客写了好久才写完,写到后面有点后劲不足,但是我相信我还是把泛型从一个新的角度表达了出来,希望研究泛型的小伙伴能够有一点点收获,泛型在实际编码过程中也是很有用的。
参考:
泛型 - 廖雪峰的官方网站 (liaoxuefeng.com) 
Java和Kotlin中泛型的协变、逆变和不变 - 简书 (jianshu.com)