泛型程序设计

泛型程序设计

写在最前
泛型 VS 强制类型转换

使用泛型机制编写的程序代码要比那些杂乱的使用Object变量,然后再进行强制类型转换的代码具有更好的安全性以及可读性

为什么要使用泛型程序设计

泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用

定义简单的泛型类

泛型类就是具有一个或者多个类型变量的类,一般而言,类型变量使用大写形式并且比较短。

public class Pair<T>{
    private T first;
    private T second;
    public Pair(){first = null;second = null;}
    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 newValue){first = newValue;}
    public void setSecond(T newValue){second = newValue;}
}

用具体的类型替换泛型类的类型变量就可以实现实例化泛型类,其实,泛型类可以看作是普通类的工厂

使用泛型类

/**
* @return return 的是一个Pair类
*/
public static Pair<String> minmax(String[] a){
    if(a == null || a.length == 0) return null;
    String min = a[0];
    String max = a[0];
    for(int i = 0;i<a.length;i++){
        if(min.compareTo(a[i]) > 0) min = a[i];
        if(max.compareTo(a[i]) < 0) max = a[i];
    }
    return new Pair<>(min,max);
}
/**
String[] words = {"Mary","had","a","little","lamb"};
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = "+mm.getFirst());
System.out.println("max = "+mm.getSecond());
*/

泛型方法

泛型方法就是一个带有类型参数的简单方法

public static <T> T getMiddle(T... a){
        return a[a.length/2];
}
/**
String middle = ArrayAlg.getMiddle("John","Q.","Public");
// 注意:如果参数是不同种类型的话,是不可以的,例如ArrayAlg.getMiddle(3.14,1729,0)编译器会报错
double middle2 = ArrayAlg.getMiddle(3.14,1729.0,0.0);
System.out.println(middle);
System.out.println(middle2);
*/

类型变量的限定

有的时候,需要对类型变量加以约束

public static <T extends Comparable> T min(T[] a){
        if(a == null || a.length ==0) return null;
        T smallest = a[0];
        for(int i = 1;i<a.length;i++) if(smallest.compareTo(a[i]) > 0 ) smallest = a[i];
        return smallest;
}

让T extends Comparable接口是为了确保泛型类型T所属的类有Comparable接口,方法为将泛型类型T限制为实现了Comparable接口的类。为泛型类型T设置限定bound

Comparable是一个接口,为什么用extend而不用implements,<T extends Comparable & Serializable> 表示T应该是绑定类型的子类型,通过&实现对泛型类型T的多个限定

泛型代码和虚拟机

写在最前:JAVA虚拟机(JVM)没有泛型类型对象,所有的对象都属于普通类

  • 虚拟机中没有泛型,只有普通的类和方法
  • 所有的类型参数都用他们的限定类型来进行替换
  • 桥方法被合称来保持多态
  • 为了保持类型安全性,必要时插入强制类型转换

类型擦除:无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type),原始类型的名字就是删除类型参数后的泛型类型名,擦出类型变量,并替换成为限定类型(无限定类型用Object)

例如,上述Pair的原始类型如下(因为T是一个无限定的类型变量,所以直接使用Object替换)

public class Pair{
    private Object first;
    private Object second;
    public Pair(){first = null;second = null;}
    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 newValue){first = newValue;}
    public void setSecond(Object newValue){second = newValue;}
}

再来一个原始类型类的转换

public class Interval<T extends Comparable & Serializable> implements Serializable{
      private T lower;
      private T upper;
      ...
      public Interval(T first,T second){
            if(first.compareTo(second) <=0){
                  lower = first;
                  upper = second;
            }else{
                  lower = second;
                  upper = first;
            }
      }
}

原始类型Interval如下所示

public class Interval implements Serializable{
      private Comparable lower;
      private Comparable upper;
      ...
      public Interval(Comparable first, Comparable second){...}
}

如果是class Interval<T extends Serializable & Comparable> 那么原始类型会用Serializable替换T

翻译泛型表达式:当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换

Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();

JVM对原始方法Pair.getFirst()方法的调用,然后将返回的Object类强制转换成为Employee类型

翻译泛型方法

类型擦除也会出现在泛型方法中

public static <T extends Comparable> T min(T[] a){}

擦除类型之后

public static Comparable min(Comparable[] a){}

有时候,类型擦除会带来复杂的问题,如下实例:

class DateInterval extends Pair<LocalDate>
{
      public void setSecond(LocalDate second){....}
}
// 类型擦除之后
class DateInterval extends Pair
{
      public void setSecond(LocalDate second){....}
}
// 存在另一个从Pair继承的setSecond方法
public void setSecond(Object second){...}

上述方法擦除就产生了问题,问题在于类型擦除与多态发生了冲突,想要解决这个问题,就需要在编译器在DateInterval类中生成一个桥方法(bridge method)

class DateInterval extends Pair<Localdate>{
      public void setSecond(Object second){setSecond((Date) second);}
}

调用遗留代码

posted @ 2020-12-07 14:14  BOTAK  阅读(113)  评论(0编辑  收藏  举报