zoukankan      html  css  js  c++  java
  • Java泛型编程

    1. 泛型类

      泛型类就是具有一个或者多个类型变量的类,在Java集合框架中大量使用了泛型类。通过泛型编程可以使编写的代码被很多不同的类型所共享,大大提高了代码的重用性。

      下面给出一个自定义泛型类的例子:

    public class Pair<T>
    {
       private T first;
       private T second;
       
       public Pair(T first,T second)
      {
         this.first = first;
         this.second = second;
      }
    
      public T getFirst()
      { 
         return first;
      }
      
      public T getSecond()
      {
        return second; 
      }
    
     public void setFirst(T first)
     {
        this.first = first; 
     }
      
     public void setSecond(T second)
     { 
        this.second = second;
     }  
     
    }

      使用普通的类名替换类型变量T就可以实例化泛型类型,如:Pair<String>,Java的泛型类类似于C++的模板类。

    2. 泛型方法

     Java还可以定义带有类型参数的方法,即泛型方法,泛型方法可以定义在泛型类中,也可以定义在普通类中。

    public class ArrayHelper
    {
        public static <T> T getMiddle(T[] array)
        {
            return array[array.length / 2];
        }
    }

     上述的泛型方法,参数是泛型数组,返回值是泛型变量,在修饰符后面跟有<T>表示这是泛型方法。调用一个泛型方法时在方法名前的"<>"加入具体类型,如:ArrayHelper.<String>getMiddle(new String[]{"left","middle","right"}) ,其实大多数情况下也可以省略<String>。

    3. 类型变量的限定

      有些时候,我们希望能使用不同的类型,但又希望这类型能满足某些约束条件,这就要依靠对类型变量的限定。

    public class ArrayHelper
    {
      public static <T> T max(T[] array)
      { 
          T  max = array[0];
          for(int i = 1; i <  array.length; i++)
            if(array[i].compareTo(max) > 0)
             max = array[i];
    return max; } }

     我们希望使用compareTo方法来比较泛型数组中的每个元素,从而选择出最大的那个元素,而这就要求类型必须实现了Comparable接口,我们就可以对类型变量T作出如下限定:

    public class ArrayHelper
    {
      public static <T extends Comparable> T max(T[] array)
      { 
          T  max = array[0];
          for(int i = 1; i <  array.length; i++)
            if(array[i].compareTo(max) > 0)
             max = array[i];
    return max; } }

     一个类型变量可以有多个限定,如: <T extends  Comparable & Serializable> 。

    4. 类型擦除

     我们定义一个泛型类型后,就可以适配多种不同的类型,然而实际上虚拟机只知道一个原始类型,例如,对于上面定义个Pair<T>,其对应的原始类型如下:

    public class Pair
    {
       private Object first;
       private Object second;
       
       public Pair(Object first,Object second)
      {
         this.first = first;
         this.second = second;
      }
    
      public Object getFirst()
      { 
         return first;
      }
      
      public Object getSecond()
      {
        return second; 
      }
    
     public void setFirst(Object first)
     {
        this.first = first; 
     }
      
     public void setSecond(Object second)
     { 
        this.second = second;
     }  
     
    }

     即将T替换成了Object类,实际上是将T替换成限定的类型。假设<T extends Comparable>,则就会将T替换成Comparable,如果有多个限定类型,则替换成第一个限定类型。如果没有限定类型,就替换成Object类,这个过程即类型擦除。

     所以泛型类编译成字节码后就是一个普通的类。

    5. 泛型类的继承规则

    (1)假设有一个print方法打印雇员对,参数是Pari<Employee>

    public void print(Pair<Employee>)
    {
       .....
    }

          Manager类是Employee类的子类,那么Pair<Manager>是Pair<Employee>的子类么?,可以传入print方法么?答案是不行,Pair<Manager>不是Pair<Employee>的子类。

    (2) 永远可以将参数化类型转换成原始类型,如:Pair  pair = new Pair<Manager>("Jack","Mike"); 这是为了与泛型之前的遗留代码能够保持衔接。

    (3)泛型类可以像普通类一样继承其他类,实现接口。如: class Pair<T>  implements Comparable 。

    6. 通配符类型

     Pair<? extends Fruit> 表示任何泛型Pair类型,它的类型参数是Fruit的子类。Pair<Fruit>和Pair<Apple>都是Pair<? extends Fruit>的子类型。

     Pair<? super Apple>表示任何泛型Pair类型,它的类型参数是Apple的父类。Pair<Fruit>和Pair<Object>都是Pair<? super Apple>的子类型。

     这样,就可以利用参数多态了,修改上面的print方法:

    public void print(Pair<? extends Fruit>)
    {
       .....
    }

     现在就可以传入Pair<Apple>和Pair<Banana>等作为参数了。

     但是对于通配符类型的多态,使用父类变量引用子类实例时,需要注意以下的问题:

    Pair<Apple>  apples = new Pair<Apple>(new Apple("apple1"),new Apple("apple2"));
    Pair<? extends Fruit>  fruits = apples;
    //下面两句调用setFirst方法编译报错,因为编译器只知道Pair中保存类型的是Fruit的子类,但不知道具体是什么类型。
    fruits.setFirst(new Apple("apple3"));    
    fruits.setFirst(new Banana("banana1"));
    //下面调用getFirst方法不会出错,因为编译器知道Pair中保存的类型一定是Fruit的子类,转换成Fruit类不会出错。
    Fruit  first = frutis.getFirst();

     即对于<? extends Type> 通配符类型,使用父类变量引用子类实例时,不能对子类实例进行写,只能读。

    Pair<Apple>  apples = new Pair<Apple>(new Apple("apple1"),new Apple("apple2"));
    Pair<? super Apple>  fruits = apples;
    fruits.setFirst(new GoodApple("apple3"));  //这一句调用setFirst方法不会出错,因为编译器知道Pair中保存的类型一定是Apple类的父类,因此,传入Apple类对象或者Apple类的子类对象都是可以的
    fruits.setFirst(new Fruit("banana1")); //传入Apple类的父类对象就会编译错误
    Fruit first = frutis.getFirst(); //编译不通过,因为编译器知道Pair中保存的类型是Apple类的父类,但不知道具体是什么类,因此,只能赋值给Object类的变量

    即对于<? super Type> 通配符类型,使用父类变量引用子类实例时,不能对子类实例进行读,只能写。

    参考资料 《Java核心技术》

  • 相关阅读:
    LightOj1054
    LightOj1028
    Docker仓库(转载)
    Dockerfile(转载)
    Docker存储卷(转载)
    容器虚拟化网络和Docker容器网络(转载)
    Docker镜像管理基础(转载)
    Docker基础用法(转载)
    docker容器技术基础入门(转载)
    Redis 3种安装部署方式
  • 原文地址:https://www.cnblogs.com/jqctop1/p/4717582.html
Copyright © 2011-2022 走看看