(java technic)Generic programming部分笔记整理(being revised)

Generic programming


1-Reason:


编写的代码可以被很多不同类型的对象所重用。
泛型提供的一个解决方案:type parameters
ArrayList类有一个类型参数用来指示元素的type:
    ArrayList<String> files=new ArrayList<String>();
更具可读性
tips:构造函数中可以省略泛型类型:
ArrayList<String> files=new ArrayList<>();
省略的type可以从变量的type推断
利用wildcard type编写出尽可能灵活的方法


2-定义简单泛型类 generic class


一个泛型类就是具有一个或多个类型变量的类。
例:pair类,只关注泛型

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;}
} 

泛型类可以有多个类型变量。如可以定义Pair类,其中第一个域和第二个域使用不同的类型:
    public class Pair<T,U>{...}
类定义中的类型变量指定方法的返回类型以及域和局部变量的类型,如:
private T first;    //uses the type variable    
    java库中,使用变量E表示集合的元素类型
             K和V分别表示表的关键字与值的类型
             T(需要时还可以用邻近的字母U和S)表示“任意类型”
用具体的类型替换类型变量就可以实例化泛型类型,如:
Pair<String>             
可以将结果想象成带有构造器的普通类和方法,或者普通类的工厂

 

3-泛型方法


定义一个带有类型参数的方法
    class ArrayAlg{
    public static <T> T getMiddle(T...a){
        return a[a.length/2];
        }
    }
这个方法是在普通类中定义的,而不是在泛型类中定义的
类型变量放在修饰符后,返回类型前
泛型方法可以定义在普通类中,也可以定义在泛型类中
当调用一个泛型方法是,在方法名前的<>内放入具体的类型:
String middle=ArrayAlg.getMiddle("John","Q.","Public");
此时,方法调用可以省略<String>类型参数。编译器用names的类型(String[])
与泛型类型 T[] 进行匹配并推断出T一定是String。
可以调用:
String middle=ArrayAlg.getMiddle("John","Q.","Public");
attention:提示错误
例:
double middle=ArrayAlg.getMiddle(3.14,1729,0);


4-类型变量的限定


类或方法有时需要对类型变量加以约束
例:

class ArrayAlg{
public static<T> T min(T[] a){    //almost correct
    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;

question:
smallest类型为T,怎样能确信T所属的类有compareTo方法?
方案是将T限制为实现了Comparable接口(只含一个方法compareTo的标准接口)的类。
通过对类型变量T设置bound(绑定\限定)实现:
public static <T extends Comparable> T min(T[] a)...
实际上Comparable接口本身就是一个泛型类型
现在,泛型min方法只能被实现了Comparable接口的类(如String、Date等)的数组调用
由于Rectangle方法没有实现Comparable接口,所以调用min将会产生编译错误
<T extends BoudingType>
表示T应该是绑定类型的subtype。T和绑定类型可以是类,也可以是接口,
因此选择关键字extends更接近概念
一个类型变量或通配符可以有多个限定
如:
T extends Comparable & Serializable        可比较&序列化
 & 分隔限定类型,"," 分隔类型变量
Java继承中,可以 根据需要拥有多个接口超类型,但限定中至多有一个类。
如果用一个类作为限定,它必须是限定列表中的第一个
例:(重新编写了一个泛型方法minmax,计算泛型数组的最大值和最小值,并返回Pair<T>)

package pair2;
import java.util.*;
public class PairTest{
    GregorianCalendar[] birthdays=
    {new GregorianCalendar(1906,Calendar,DECEMBER,9),//G.H
     new GregorianCalendar(1815,Calendar,DECEMBER,10),//A.L
     new GregorianCalendar(1903,Calendar,DECEMBER,3),//J.N
     bew GregorianCalendar(1910,Calendar,JUNE,22),//K.Z
     }
    Pair<GregorianCalendar>mm=ArrayAlg.minmax(birthdays);
    System.out.println("min="+mm.getFirst().getTime());
    System.out.println("max="+mm.getSecond().getTime());
    }
}

class ArrayAlg{
/**Gets the minimun and maximun of an array of objects of type T.
    @param a an array of ibjects of type T
    @return a pair with the min and max value,or null if a is null or empty
*/
public static <T extends Comparable> Pair<T> minmax(T[] a){
if(a==null || a.length==0) return null;
T min=a[0];
T max=a[0];
for(int i=1;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);
    }
}

5-泛型代码和虚拟机


定义一个泛型类型,都自动提供一个相应的raw type,原始类型的名字就是删去类型参数后的泛型类型名。
erased类型变量,并替换为限定类型(无限定的变量用Object)
例:(Pair<T>的原始类型如下)

public class Pair{
    private Object first;
    private Object second;
    
    public Pair(Object first,Object second){
        thie.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;}
    }

因为T是一个无限定的变量,所以直接用Object替换
结果是一个普通的类。
在程序中可以包含不同类型的Pair,如Pair<String>或Pair<GregorianCalendar>。
而擦除类型后就变成原始的Pair类型了
原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换
如,类Pair<T>中的类型变量没有显式的限定,因此原始类型用Objext替换T,假定声明一个不同的类型

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){...}
            }     

翻译泛型表达式:
当程序调用泛型方法,如果擦除返回类型,编译器插入强制类型转换。如
Pair<Employee> buddies=...;
Employee buddy=buddies.getFirst();
擦除gerTirst的返回类型后将返回Object类型。编译器自动插入Employee的强制类型转换,即,编译器把这个方法调用翻译为两条虚拟机指令:
*对原始方法Pair.getFirst的调用
*将返回的Object类型强制转换为Employee类型
当存取一个泛型域是也要插入强制类型转换。假设Pair类的first域和second域都是公有的。表达式:
Employee buddy=buddies.first;
也会在结果字节码中插入强制类型转换
翻译泛型方法:
类型擦除也会出现在泛型方法中
public static<T extends Comparable> T min(T[] a)
擦除类型后,只剩下一个方法:
public static Comparable min(Comparable[] a)
attention:类型参数T已经被擦除了,只留下了限定类型Comparable
方法的擦除带来了两个复杂的问题,如下:

class DateInterval extends Pair<Date>{
    public void setSecond(Date second){
        if(second.compareTo(getFirst())>=0)
            super.setSecond(second);
        }
        ...
    }

 一个日期区间是一对Date对象,并且需要覆盖这个方法来确保第二个值永远不小于第一个值,这个类擦除后变成

 class DateInterval extends Pair {//after erasure
     public void setSecond(Date second){...}
    ...
}

存在另一个从Pair继承的setSecond方法,即:
public void setSecond(Object second)
这显然是一个不同的方法,因为它有一个不同类型的参数——Object,instead of Date.While it should be same.Think of this:
    DateInterval interval=new DateInterval(...);
    Pair<Date> pair=interval;    //OK-assignment to superclass
    pair.setSecond(aDate);
希望对setSecond的调用具有多态性,并调用最合适的方法,由于Pair引用DateInterval对象,所以应该调用DateIntervalSecond。
the contradiction:类型擦除与多态发生了冲突。    
需要编译器在DateInterval类中生成一个bridge method:
public void setSecond(Object second){setSecond(Date) second;}
————————————————————————————————————
pair.setSecond(aDate)
变量pair已经声明为类型Pair<Date>,并且这个类型只有一个方法setSecond,
即setSecond(Object)。虚拟机用pair引用的对象调用这个方法。该对象是DateInterval类型的,因而将会调用DateInterval.setSecond(Object)方法。
这个方法是合成的桥方法。它调用DateInterval.setSecond(Date)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>