zoukankan      html  css  js  c++  java
  • Java 之 泛型的使用形式

    泛型的使用形式有两种:

      1、泛型类/泛型接口

      2、泛型方法

    一、泛型类/接口

      下面先来看下 JDK1.5改写后的  ArrayList 类、Iterator 接口、Map的代码片段。

      

       从上面的代码中,可以看出在定义接口、类时指定类型形参,如上面的E、K、V。

      当使用这些集合时,就可以为E、K、V指定具体的类型实参。

      Demo:

     1 import java.util.HashMap;
     2 import java.util.Iterator;
     3 import java.util.Set;
     4 
     5 public class TestHashMap {
     6     public static void main(String[] args) {
     7         HashMap<Integer,String> map = new HashMap<Integer,String>();
     8         map.put(1, "Hello");
     9         map.put(2, "World");
    10         Set<Integer> keySet = map.keySet();
    11         Iterator<Integer> iter = keySet.iterator();
    12         while(iter.hasNext()){
    13             Integer key = iter.next();
    14             System.out.println(key + "->" + map.get(key));
    15         }
    16     }
    17 }

      1、如何定义泛型类、接口?

        我们可以为任何类和接口增加泛型声明,并不是只有集合类才可以使用泛型声明。

        泛型形参的命名一般使用单个的大写字母,如果有多个类型形参,那么使用逗号分隔。

        语法格式:

    【修饰符】  class  类名<泛型形参列表>{}
    【修饰符】  interface  接口名<泛型形参列表>{}
    

        Demo:定义学生类,其中的学生成绩可以是如下各种类型:整数、小数、字符串(优秀,良好,合格,不合格)

     1 public class TestStudent {
     2     public static void main(String[] args) {
     3         Student<Integer> s1 = new Student<Integer>("张三",95);
     4         Student<String> s2 = new Student<String>("张三","优秀");
     5         Student<Double> s3 = new Student<Double>("张三",80.5);
     6     }
     7 }
     8 class Student<T>{
     9     private String name;
    10     private T score;
    11     
    12     public Student() {
    13         super();
    14     }
    15     public Student(String name, T score) {
    16         super();
    17         this.name = name;
    18         this.score = score;
    19     }
    20     public String getName() {
    21         return name;
    22     }
    23     public void setName(String name) {
    24         this.name = name;
    25     }
    26     public T getScore() {
    27         return score;
    28     }
    29     public void setScore(T score) {
    30         this.score = score;
    31     }
    32     @Override
    33     public String toString() {
    34         return "姓名:" + name + ", 成绩:" + score;
    35     }
    36 }

          从上面的代码中,可以看出可以在定义接口、类时指定类型形参,类型形参在整个接口或类中可以当成类型使用,几乎所有可以使用其他普通类型的地方都可以使用这种类型形参,如:属性类型、方法的形参类型、方法返回值类型等。

        Tips:

    当创建带泛型声明的类时,为该类定义构造器时,构造器名还是原来的类名,不需要增加泛型声明。例如:Student<T>类定义的构造器依然是Student(),而不是Student<T>,但调用构造器时缺可以使用Student<T>的形式,而且应该为T类型形参传入实际的类型实参。

      2、泛型类或泛型接口上的<泛型形参>这个类型可以用在哪些地方?

        (1)可以用于属性、方法的数据形参、局部变量等的类型

        (2)不能用于声明静态变量,不能用于静态成员上

            原因:因为静态成员的初始化是随着类的初始化而初始化的,此时泛型实参无法指定,那么泛型形参的类型就不确定。所以请不要在静态成员上使用类或接口上的泛型形参。

      3、如何为泛型类、泛型接口指定泛型实参?

        (1)泛型实参的要求

           泛型实参必须是引用数据类型,不能是基本数据类型。

           

        (2)什么时候指定泛型实参

            a、在用泛型类、接口声明变量时  

    1 class EmployeeManager{
    2     private ArrayList<Employee> list;
    3 }
    4 //接口中的方法
    5 public static void test(ArrayList<String> list){
    6     //....省略代码
    7 }

            b、在继承泛型类或实现泛型接口时,如果子类不延续使用该泛型,那么必须明确指定实际类型,此时子类不再是泛型类了。

      

            c、在创建泛型类对象时

    ArrayList<String> list = new ArrayList<String>();
    ArrayList<String> list = new ArrayList<>();  (JDK1.7之后支持这种简化写法)

      4、如何延续使用父类、父接口的泛型形参?

         如果继承泛型类、实现泛型接口时,想要继续保留父类、父接口的泛型,必须在父类、父接口和子类、子接口中都要保留泛型。

        Demo:

        集合中大量是这种情况:

     

     

      5、设定泛型形参的上限?

        语法格式:

    <T extends 上限>     T的类型实参只能上限本身或上限的子类
    <T extends 上限1 &  上限2 &....>   如果多个上限,都要满足
    

          Demo : 需求,定义学生类,其中的学生的成绩可以是数字类型,如Integer、Float、Double等

     1 class Student<T extends Number> {
     2     private String name;
     3     private T score;
     4 
     5     public Student() {
     6         super();
     7     }
     8 
     9     public Student(String name, T score) {
    10         super();
    11         this.name = name;
    12         this.score = score;
    13     }
    14 
    15     public String getName() {
    16         return name;
    17     }
    18 
    19     public void setName(String name) {
    20         this.name = name;
    21     }
    22 
    23     public T getScore() {
    24         return score;
    25     }
    26 
    27     public void setScore(T score) {
    28         this.score = score;
    29     }
    30 
    31     @Override
    32     public String toString() {
    33         return "姓名:" + name + ", 成绩:" + score;
    34     }
    35 }

        如果泛型形参没有设定上限,那么泛型实参可以是任意引用数据类型。如果形参设定了上限(如:T extends 父类上限),那么只能指定为该父类本身或其各子类类型。

        如:

    1 public class TestStudentUpperBound {
    2     public static void main(String[] args) {
    3         Student<Integer> s1 = new Student<Integer>("张三", 95);
    4         Student<Double> s3 = new Student<Double>("张三", 80.5);
    5         //以下代码编译报错,因为String不是Number的子类
    6         Student<String> s2 = new Student<String>("张三", "优秀");
    7     }
    8 }

        在一种更极端的情况下,程序需要为形参设定多个上限(至多有一个父类上限,可以多个接口上限)表名该类型形参必须是其父类的子类(包括是父类本身也行),并且实现多个上限接口。

        如:

    1 class Student<T extends Number & Comparable & java.io.Serializable> {
    2     //...省略其他代码
    3 }

        与类同时继承父类、实现接口类似的是:为类型形参指定多个上限时,所有的接口上限必须位于类上限之后,也就是说,如果需要为类型形参指定类上限,类上限必须位于第一位。

      6、泛型的形参一般代表什么的类型?

        如:

          ArrayList<E>: 这个E为element元素的缩写,代表集合的元素的类型

          Map<K,V>:这个K代表Key的类型,V代表value的类型。

          Comparable<T>:这个T为Type,表示要与当前对象比较的另一个对象的类型

          Student<T>:这个T代表成绩的类型。

        在声明泛型类或泛型接口,泛型形参最好见名知意。

    二、泛型方法

      1、什么情况下需要声明泛型方法?

        上面介绍了在定义类、接口时可以使用类型形参,在该类的方法和属性定义、接口的方法定义中,这些类型形参可被当成普通类型来用。还有另外一些情况:

        (1)如果定义类、接口是没有使用类型形参,但是某个方法定义时,想要自己定义类型形参;

        (2)类和接口上的类型形参是不能用于静态方法中,那么当某个静态方法想要定义类型形参;

         JDK1.5之后,提供了泛型方法的支持。

      2、如何声明泛型方法?

        语法格式:

    [修饰符]  <泛型形参列表>  返回类型  方法名([形参列表])  抛出的异常列表{
         //方法体...
    }
    

         其中泛型形参列表,可以是一个或多个,如果多个,使用逗号分隔,和定义泛型类、接口时一样,而且<泛型形参列表>必须在修饰符和返回值类型之间。

         与接口、类声明中定义的泛型形参不同,方法声明中定义的泛型形参只能在当前方法中使用,和其他方法无关。

         与接口、类声明中定义的泛型形参不同,方法声明中定义的泛型形参无需显式传入实际类型参数,编译器可以根据实参类型直接推断形参的实际类型。

         Demo:编写一个方法负责将一个数组的所有元素添加到一个Collection集合中

    1     public static void fromArrayToCollection(Object[] a,Collection<Object> c){
    2         for (Object object : a) {
    3             c.add(object);
    4         }
    5     }

        上面的这个方法没有任何问题,关键在于上面方法中的 c 形参,它的数据类型是 Collection<Object>。正如上面所说,Collection<Object> 不是 Collection<String> 类的父类——所以这个方法的功能非常有限,形参c只支持Collection<Object>类型的实参,不接收其他类型的实参。

    1         String[] array = {"hello","world","java"};
    2         Collection<String> coll = new ArrayList<String>();
    3         //编译报错,因为Collection<Object>形参不接收Collection<String>实参,因为它俩不是父子类关系
    4         fromArrayToCollection(array,coll);

        为了解决上面的问题可以使用泛型方法。

     1     public static <T> void fromArrayToCollection(T[] a,Collection<T> c){
     2         for (T object : a) {
     3             c.add(object);
     4         }
     5     }
     6     public static void main(String[] args) {
     7         String[] array = {"hello","world","java"};
     8         Collection<String> coll = new ArrayList<String>();
     9         fromArrayToCollection(array,coll);
    10     }

      3、什么时候给泛型方法指定类型实参?

        调用这个方法时,编译器会根据方法的实参的类型,来确定泛型的类型实参的具体类型

      4、如何设定泛型形参的上限?

        泛型方法的<泛型形参列表>中的类型也可以指定上限

    <T extends 上限>   T的类型实参只能上限本身或上限的子类
    <T extends 上限1 &  上限2 & 。。。。>   如果多个上限,都要满足

        Demo:

     1 public abstract class Graphic{    //图形类
     2     public abstract double getArea();
     3 }
     4 
     5 //圆类
     6 public class Circle extends Graphic{
     7     private double radius;
     8 
     9     public Circle(double radius) {
    10         super();
    11         this.radius = radius;
    12     }
    13 
    14     @Override
    15     public double getArea() {
    16         return Math.PI * radius * radius;
    17     }    
    18 }
    19 
    20 //矩形类
    21 public class Rectangle extends Graphic{
    22     private double length;
    23     private double width;
    24     public Rectangle(double length, double width) {
    25         super();
    26         this.length = length;
    27         this.width = width;
    28     }
    29     @Override
    30     public double getArea() {
    31         return length * width;
    32     }
    33 }
    34 
    35 import java.util.ArrayList;
    36 import java.util.List;
    37 
    38 public class TestGraphic {
    39         //定义一个方法,求多个图形的面积
    40     public static <T extends Graphic> void printArea(List<T> graphics){
    41         for (T t : graphics) {
    42             System.out.println(t.getArea());
    43         }
    44     }
    45     public static void main(String[] args) {
    46         ArrayList<Circle> cList = new ArrayList<Circle>();
    47         cList.add(new Circle(1.2));
    48         cList.add(new Circle(2.3));
    49         printArea(cList);
    50         
    51         ArrayList<Rectangle> rList = new ArrayList<Rectangle>();
    52         rList.add(new Rectangle(1,2));
    53         rList.add(new Rectangle(2,3));
    54         printArea(rList);
    55     }
    56 }

      Tips:其实没有设定泛型形参上限的,可以看成它的上限默认是Object。

     

  • 相关阅读:
    JavaScript:for循环中let与var变量的绑定
    ECAMScript中的let和const
    HTML中的a标签
    jQuery中的事件处理(移除绑定)
    jQuery中的事件处理(事件绑定)
    jQuery中的事件处理(页面加载响应事件)
    jQuery对元素的CSS样式操作(通过修改CSS属性实现)
    jQuery对元素的CSS样式操作(通过修改CSS类实现)
    jQuery对DOM节点进行操作(包裹节点)
    jQuery对DOM节点进行操作(遍历节点)
  • 原文地址:https://www.cnblogs.com/niujifei/p/12191500.html
Copyright © 2011-2022 走看看