Java 泛型详解

lilv3000 2年前
   <p>在日常的开发中,我们会看到别人的框架很多地方会使用到泛型,泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。本篇博客我们就来详细解析一下泛型的知识。</p>    <h2>泛型类定义及使用</h2>    <p>使用泛型有什么好处呢?首先我们先看一个例子,假设我们有两个类,代码如下:</p>    <pre>  <code class="language-java">#StringClass   public class StringClass {      private String x ;      private String y ;        public String getY() {          return y;      }        public void setY(String y) {          this.y = y;      }        public String getX() {          return x;      }        public void setX(String x) {          this.x = x;      }  }</code></pre>    <pre>  <code class="language-java">#IntClass   public class IntClass {      private int x ;      private int y ;        public int getY() {          return y;      }        public void setY(int y) {          this.y = y;      }        public int getX() {          return x;      }        public void setX(int x) {          this.x = x;      }  }</code></pre>    <p>观察上面两个类StringClass 和IntClass,他们除了变量类型不一样,一个是String一个是int以外,其它并没有什么区别!那我们能不能合并成一个呢?通过泛型就可以解决,首先看一下泛型的类是怎么定义的:</p>    <pre>  <code class="language-java">public class ObjClass<T> {        private T x ;      private T y ;        public T getX() {          return x;      }        public void setX(T x) {          this.x = x;      }        public T getY() {          return y;      }        public void setY(T y) {          this.y = y;      }  }</code></pre>    <p>那么这时候上面的两个类就可以通过泛型的设置,相应生成</p>    <pre>  <code class="language-java">ObjClass<String> stringClass = new ObjClass<String>();          stringClass.setX("haha");            ObjClass<Integer> intClass = new ObjClass<Integer>();          intClass.setX(100);            Log.d("yyy", "stringClass:" + stringClass.getX() + ",intClass:" + intClass.getX());</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0308b90923573d4526442b923b3fa23c.png"></p>    <p>从结果中可以看到,我们通过泛型实现了开篇中StringClass类和IntClass类的效果。</p>    <p>接下来介绍泛型如何定义及使用:</p>    <p>1.首先需要定义泛型:ObjClass</p>    <p>ObjClass ,即在类名后面加一个尖括号,括号里是一个大写字母。这里写的是T,其实这个字母可以是任何大写字母,无论使用哪个字母,意义都是相同的。</p>    <p>2.在类中使用泛型</p>    <p>这个T表示派生自Object类的任何类,比如String,Integer,Double等等。这里要注意的是,T一定是派生于Object类的。</p>    <pre>  <code class="language-java">private T x ;      private T y ;        public T getX() {          return x;      }        public void setX(T x) {          this.x = x;      }        public T getY() {          return y;      }        public void setY(T y) {          this.y = y;      }</code></pre>    <p>3.使用泛型类</p>    <p>泛型类的使用代码如下:</p>    <pre>  <code class="language-java">ObjClass<String> stringClass = new ObjClass<String>();          stringClass.setX("haha");            ObjClass<Integer> intClass = new ObjClass<Integer>();          intClass.setX(100);</code></pre>    <p>首先,需要构造一个实例:</p>    <pre>  <code class="language-java">ObjClass<String> stringClass = new ObjClass<String>();</code></pre>    <p>泛型类的构造则需要在类名后添加上,即一对尖括号,中间写上要传入的类型。</p>    <p>因为我们构造时,是这样的:ObjClass,所以在使用的时候也要在ObjClass后加上类型来定义T代表的意义。</p>    <p>尖括号中,你传进去的是什么,T就代表什么类型。这就是泛型的最大作用,我们只需要考虑逻辑实现,就能拿给各种类来用。</p>    <h2>多泛型变量定义</h2>    <p>1.多泛型变量定义</p>    <p>我们不止可以在类中设置一个泛型变量T,还可以声明多个泛型变量,写法如下:</p>    <pre>  <code class="language-java">public class ObjClass<T,U></code></pre>    <p>也就是在原来的T后面用逗号隔开,写上其它的任意大写字母即可,如果还有多个,依然使用逗号分隔开即可,则我们前面定义的泛型类就会变成下面这样:</p>    <pre>  <code class="language-java">public class ObjClass<T,U> {        private T x ;      private U y ;        public T getX() {          return x;      }        public void setX(T x) {          this.x = x;      }        public U getY() {          return y;      }        public void setY(U y) {          this.y = y;      }  }</code></pre>    <pre>  <code class="language-java">ObjClass<String,Integer> stringClass = new ObjClass<String,Integer>();          stringClass.setX("haha");          stringClass.setY(100);</code></pre>    <p>从上面的代码中,可以明显看出,就是在新添加的泛型变量U用法与T是一样的。</p>    <p>2.泛型的字母规范</p>    <p>虽然在类中声明泛型任意字母都可以,但为了可读性,最好遵循以下的规范:</p>    <pre>  <code class="language-java">E — Element,常用在java Collection里,如:  List<E>,Iterator<E>,Set<E>   K,V — Key,Value,代表Map的键值对   N — Number,数字   T — Type,类型,如String,Integer等等</code></pre>    <h2>泛型接口定义及使用</h2>    <p>在接口上定义泛型与在类中定义泛型是一样的,代码如下:</p>    <pre>  <code class="language-java">interface MsgClass<T> {      public T getMsg() ;      public void setMsg(T x);  }</code></pre>    <p>我们可以利用泛型类来构造填充泛型接口</p>    <pre>  <code class="language-java">public class Message<T,U> implements MsgClass<T>{        private T msg;      @Override      public T getMsg() {          return msg;      }        @Override      public void setMsg(T msg) {          this.msg = msg;      }  }</code></pre>    <p>在这个类中,我们构造了一个泛型类Message,然后把泛型变量T传给了MsgClass,这说明接口和泛型类使用的都是同一个泛型变量。</p>    <p>我们还可以构造一个多个泛型变量的类,并继承自MsgClass接口:</p>    <pre>  <code class="language-java">public class Message<T,U> implements MsgClass<T>{      private U name;      private T msg;      @Override      public T getMsg() {          return msg;      }        @Override      public void setMsg(T msg) {          this.msg = msg;      }        public U getName() {          return name;      }        public void setName(U name) {          this.name = name;      }  }</code></pre>    <h2>泛型函数定义及使用</h2>    <p>我们不但可以在类声明中使用泛型,还可以在函数声明中也使用泛型,使用如下:</p>    <pre>  <code class="language-java">public class ObjClass {      //静态函数      public static <T> void StaticMethod(T a) {        }        //普通函数      public <T> void OrgnicMethod(T a) {        }    }</code></pre>    <p>上面分别是静态泛型函数和常规泛型函数的定义方法,与以往方法的唯一不同点就是在返回值前加上来表示泛型变量。</p>    <p>无论哪种泛型方法都有两种使用方法:</p>    <pre>  <code class="language-java">//静态方法  ObjClass.StaticMethod("adfdsa");//使用方法一  ObjClass.<String>StaticMethod("adfdsa");//使用方法二  //常规方法  ObjClass objClass = new ObjClass();  objClass.OrgnicMethod(new Integer(111));//使用方法一  objClass.<Integer>OrgnicMethod(new Integer(111));//使用方法二</code></pre>    <p>方法一,隐式传递了T的类型,这种隐式的传递方式,代码不利于阅读和维护。因为从外观根本看不出来你调用的是一个泛型函数。</p>    <p>方法二,例如上面例子中,将T赋值为Integer类型,这样OrgnicMethod(T a)传递过来的参数如果不是Integer那么编译器就会报错。</p>    <p>当然泛型函数的返回值也可以使用泛型表示:</p>    <pre>  <code class="language-java">public static <T> List<T> parseArray(String response,Class<T> object){        List<T> modelList = JSON.parseArray(response, object);        return modelList;    }</code></pre>    <p>函数返回值是List类型。和void的泛型函数不同,有返回值的泛型函数要在函数定义的中在返回值前加上标识泛型;还要说明的是,上面中,使用Class传递泛型类Class对象</p>    <h2>泛型数组</h2>    <p>泛型同样可以用来定义在数组上</p>    <pre>  <code class="language-java">//定义            public static <T> T[] fun1(T...msg){  // 接收可变参数                  return msg ;            // 返回泛型数组              }          //使用            public static void main(String args[]){              Integer i[] = fun1(8,9,8,44) ;              Integer[] result = fun1(i) ;          }</code></pre>    <p>定义了一个静态函数,然后定义返回值为T[],参数为接收的T类型的可变长参数。</p>    <h2>泛型的通配符</h2>    <p>在开发中对象的引用传递(向上向下传递)是最常见的,但是,在泛型的操作中,在进行引用传递的时候泛型类型必须匹配才可以传递,否则不能传递。</p>    <p>例如,如下没有进行泛型类型匹配,一个是String,一个是Object类型。</p>    <pre>  <code class="language-java">class Info<T>{      private T var ;        // 定义泛型变量      public void setVar(T var){          this.var = var ;      }      public T getVar(){          return this.var ;      }      public String toString(){             return this.var.toString() ;      }  };    public class demo1 {          public static void main(String args[]) {              // 使用String为泛型类型              Info<String> i = new Info<String>();                      i.setVar("ABCD");              //把String泛型类型的i对象传递给Object泛型类型的temp。              fun(i);                                                                                      }            // 接收Object泛型类型的Info对象          public static void fun(Info<Object> temp) {                      System.out.println("内容:" + temp);          }      }</code></pre>    <p>编译发生错误。</p>    <pre>  <code class="language-java">Exception in thread "main" java.lang.Error: Unresolved compilation problem:       The method fun(Info<Object>) in the type demo1 is not applicable for the arguments (Info<String>)        at Thread1.demo1.main(demo1.java:18)</code></pre>    <p>泛型对象进行引用传递的时候,类型必须一致,如果非要传递,则可以将fun方法中Info参数的泛型取消掉(变成 void fun(Info temp))。、</p>    <p>以上确实改进了功能,但是似乎不是很妥当,毕竟之前指定过泛型。</p>    <p>以上程序在fun()方法中使用 "Info<?>" 的代码形式,表示可以使用任意的泛型类型对象,这样的话fun()方法定义就合理了,但是使用以上方法也有需要注意的地方,</p>    <p>即:如果使用“?“接收泛型对象的时候,则不能设置被泛型指定的内容。</p>    <pre>  <code class="language-java">class Info<T>{      private T var ;              public void setVar(T var){          this.var = var ;      }      public T getVar(){          return this.var ;      }      public String toString(){              return this.var.toString() ;      }  };  public class GenericsDemo{      public static void main(String args[]){          Info<String> i = new Info<String>() ;                 i.setVar("ABCD") ;                                      fun(i) ;      }      public static void fun(Info<?> temp){                  System.out.println("内容:" + temp) ;      }  };</code></pre>    <p>如果使用”?“意味着可以接收任意的内容,但是此内容无法直接使得用”?“修饰的泛型的对象进行修改。如下就会出问题:</p>    <pre>  <code class="language-java">class Info<T>{      private T var ;              public void setVar(T var){          this.var = var ;      }      public T getVar(){          return this.var ;      }      public String toString(){             return this.var.toString() ;      }  };  public class demo1{      public static void main(String args[]){          Info<?> i = new Info<String>() ;                 i.setVar("ABCD") ;                                  }  };</code></pre>    <p>运行结果:</p>    <pre>  <code class="language-java">Exception in thread "main" java.lang.Error: Unresolved compilation problem:       The method setVar(capture#1-of ?) in the type Info<capture#1-of ?> is not applicable for the arguments (String)        at Thread1.demo1.main(demo1.java:17)</code></pre>    <p>在使用”?“只能接收,不能修改。</p>    <h2>泛型的上限</h2>    <pre>  <code class="language-java">class Info<T>{      private T var ;              public void setVar(T var){          this.var = var ;      }      public T getVar(){          return this.var ;      }      public String toString(){              return this.var.toString() ;      }  };  public class GenericsDemo{      public static void main(String args[]){          Info<Integer> i1 = new Info<Integer>() ;                  Info<Float> i2 = new Info<Float>() ;                      i1.setVar(30) ;                                              i2.setVar(30.1f) ;                                          fun(i1) ;          fun(i2) ;      }      public static void fun(Info<? extends Number> temp){    // 只能接收Number及其Number的子类          System.out.print(temp + "、") ;      }  };</code></pre>    <p>运行成功。但是,如果传入的泛型类型为String的话就不行,因为String不是Number子类。</p>    <p>在类中使用泛型上限。</p>    <pre>  <code class="language-java">class Info<T extends Number>{    // 此处泛型只能是数字类型      private T var ;              public void setVar(T var){          this.var = var ;      }      public T getVar(){          return this.var ;      }      public String toString(){             return this.var.toString() ;      }  };  public class demo1{      public static void main(String args[]){          Info<Integer> i1 = new Info<Integer>() ;        // 声明Integer的泛型对象      }  };</code></pre>    <p>如果在使用Info的时候设置成String类型,则编译的时候将会出现错误(String不是Number子类)</p>    <p>注意:利用 <? extends Number> 定义的变量,只可取其中的值,不可修改</p>    <p>原因如下:</p>    <p>因为Info的类型为 Info</p>    <h2>泛型的下限</h2>    <p><? super XXX> 表示填充为任意XXX的父类</p>    <pre>  <code class="language-java">class Info<T>{      private T var ;              public void setVar(T var){          this.var = var ;      }      public T getVar(){          return this.var ;      }      public String toString(){              return this.var.toString() ;      }  };  public class GenericsDemo21{      public static void main(String args[]){          Info<String> i1 = new Info<String>() ;        //           Info<Object> i2 = new Info<Object>() ;        //           i1.setVar("hello") ;          i2.setVar(new Object()) ;          fun(i1) ;          fun(i2) ;      }      public static void fun(Info<? super String> temp){    // 只能接收String或Object类型的泛型,String类的父类只有Object类          System.out.print(temp + "、") ;      }  };</code></pre>    <p>Object类和String类都是String的父类,所有运行成功,但是如果此时用Integer则会出错,因为integer并不是String父类。</p>    <p>注意:使用super通配符:能存不能取</p>    <p>如何理解呢?假设有3个类,继承关系如下:</p>    <pre>  <code class="language-java">class CEO extends Manager {    }      class Manager extends Employee {    }      class Employee {    }</code></pre>    <p>然后书写如下代码:</p>    <pre>  <code class="language-java">List<? super Manager> list;    list = new ArrayList<Employee>();    //存    list.add(new Employee()); //编译错误    list.add(new Manager());    list.add(new CEO());</code></pre>    <p>为什么而list.add(new Employee());是错误的?</p>    <p>因为list里item的类型是</p>    <pre>  <code class="language-java">List<Employee> list = new ArrayList<Employee>();    list.add(new Manager());    list.add(new CEO());</code></pre>    <p>在这里,正因为Manager和CEO都是Employee的子类,在传进去list.add()后,会被强制转换为Employee!</p>    <p>现在回过头来看这个:</p>    <pre>  <code class="language-java">List<? super Manager> list;    list = new ArrayList<Employee>();    //存    list.add(new Employee()); //编译错误    list.add(new Manager());    list.add(new CEO());</code></pre>    <p>编译器无法确定 <? super Manager> 的具体类型,但唯一可以确定的是Manager()、CEO()肯定是 <? super Manager> 的子类,所以肯定是可以add进去的。但Employee不一定是 <? super Manager> 的子类,所以不能确定,不能确定的,肯定是不允许的,所以会报编译错误。</p>    <p>最后强调一下, List<? super Manager> list取出的只能是Object 类型,这里虽然看起来是能取的,但取出来一个Object类型,是毫无意义的。所以才有了“super通配符:能存不能取”的结论。</p>    <p>总结1)使用?可以接收任意泛型对象。</p>    <p>2)泛型的上限:?extends 类型(能取不能存)。</p>    <p>3)泛型的下限:?super 类型? super 通配符(能存不能取)。</p>    <p> </p>    <p>来自:http://blog.csdn.net/u012124438/article/details/69487962</p>    <p> </p>