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。

 

原文地址:https://www.cnblogs.com/niujifei/p/12191500.html