Java泛型

泛型笔记
1.泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用。
   
2. 在Java中在没增加泛型之前:
    ArrayList array = new ArrayList();
    array.add(100);
    array.add("dfjd");
无论是在编译时,还是在运行时,该代码都不会出错,因为ArrayList类在Java没设置泛型之前,所添加的类型为:Object
在原始的ArrayList类中会出现两个问题:
  1. 取一个值时,必须进行强制转换
                String s = (String)array.get(1); 
       2.String s = (String)array.get(0);         //因为get(0)中的值为int类型,但int 与String类型之间不能进行直接的转换,所以会报错
 
3.解决方式----类型参数
    在ArrayList类有一个类型参数用来指示元素的类型:
        ArrayList<String> array = new ArrayList<String>();         //这就告诉,array对象中只能存放String类型的元素
    构造函数中可以省略泛型类型:
        ArrayList<String> array = new ArrayList<>();       //因为编译器能够推断出
类型参数的魅力:使得程序具有更好的可读性和安全性
 
4.定义简单的泛型
   (1). 一个泛型类就是具有一个或多个类型变量的类
public class Pair <T> {         //在类名之后使用<>将参数类型T括起来,这个普通的类就变成了泛型类
        private T first;
        private T second;
       
        public Pair(T first,T second){     //当实例化Pair对象时,指定类类型,则T会替换成该类类型
              this. first = first;
              this. second = second;
       }
        public T getFirst() {
             System.out.println("这是父类中的getFirst方法");
              return first;
       }
        public void setFirst(T first) {
              this. first = first;
       }
        public T getSecond() {
              return second;
       }
        public void setSecond(T second) {
             System.out.println("这是父类中的setSecond方法");
              this. second = second;
       }
        @Override
        public String toString() {
              return "Pair类中的first的值为:" + this.getFirst() + '\n' + "Pair类中的second的值为:" + this.getSecond();
       }
}
    (2).具有多个类型变量的泛型类
public  class Pair <T,U>{
.....
}
    (3). 泛型类的作用:类定义中的类型变量指定 方法的返回类型以及 或者 局部变量的类型
    (4).类型变量使用大写形式,且比较短,在Java中,使用E表示集合的元素类型,K和V分别表示表的关键字与值的类型,普通的泛型类的参数类型一般是"T"
    (5).在实例化泛型类的时候要指定类型用来代替参数类型
            如: Pair<String> p = new Pair<>("123","456");   //传入的实参类型必须与指定的参数类型一致,即为String
            即: Pair类中的域,构造方法,get/set方法中的T都用String代替了
注意:
            1).用来代替参数类型的类型必须是类类型,不能是基本类型(如int,double等)
            2).不能使用instanceof对参数类型进行类型的检验  如: T instanceof  String  (这样是错误的)
实例:
public class PairFunctin {
        public static Pair<String> minMax(String[] a){          //Pair类中的泛型类被String类替代
              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<String>( min, max);
       }
}
 
public class PairTest {
        public static void main(String[] args) {
             String[] strings  = { "dfdfd", "reiuuu", "wuee", "oerooi", "fwpjsj"};
             Pair<String> pair = PairFunctin. minMax( strings);
             System. out.println( pair.toString());            //Pair类中重写了toString方法
       }
}
结果为:
Pair类中的first的值为:dfdfd
Pair类中的second的值为:wuee
 
5.泛型 方法
        注意:上面的Pair泛型类中的构造方法,get/set方法都不是泛型方法,因为泛型方法有其自身定义的语法
       定义泛型方法
        public  class PairFunction[<T>] { 
            public [static] <T,[U]> T/[U] getStaticMiddle(T[] a){
                    ....
            }
        }
        (1).public  class PairFunction[<T>]-----说明泛型方法可以定义在普通类或者泛型类中
        (2).public [static] <T,[U]> T/[U]/[类类型] getStaticMiddle(T[] a)
                1).泛型方法的类型变量(这里是<T,[U]>,[U]表面U是可选的,即一个泛型方法可以带有多个类型变量)放在修饰符(这里是public [static]) 的后面,返回类型(这里是T/[U]/[类类型])的前面
                2).说明静态或者普通的方法都可以定义成泛型方法
                3).public [static] <T> T getStaticMiddle( U[] a)---此方法会报错,因为U没有在定义泛型方法时进行声明,即未在修饰符(public [static])和返回类型(T)之间声明, 未声明的类型变量不能够使用,使用必须声明
                4).与泛型类的参数类型T的定义一样,此处的T可以随便写入为任意字符(如U)
        (3).在普通类中使用泛型方法
实例:
       /*
        * 普通方法中使用泛型方法
        */
        @SuppressWarnings( "unchecked")
        public <T> T getMiddle(T[] a){
             
              if( a == null || a. length == 0){
                     return null;
             }
             String string = "";               //string = a.toString();时,结果是Object--->String,说明在编译时,就进行了参数类型擦除
              for ( int i = 0; i < a. length; i++) {
                     string += a[ i];
             }
              return (T) string;
       }
        /*
        * 静态方法中使用泛型方法---下面使用的形式,在普通方法中一样可以使用,不在一一说明
        */
        public static <T> String getStaticMiddle(T[] a){        //传入的类型与返回的类型参数一样
              if ( a == null || a. length == 0) {
                     return null;
             }
             String string =(String) a[0];
              return string;
       }
        public static <T> Integer getStaticMiddle1(T[] a){       //返回类型可以与传入的类型不一致
              if( a == null || a. length == 0){
                     return null;
             }
             Integer integer = Integer. valueOf((String) a[1]);
              return integer;
       }
        @SuppressWarnings( "unchecked")
        public static <T> T getStaticMiddle2(T[] a){              
              if( a == null || a. length == 0){
                     return null;
             }
             String string = "";               //string = a.toString();时,结果是Object--->String,说明在编译时,就进行了参数类型擦除
              for ( int i = 0; i < a. length; i++) {
                     string += a[ i];
             }
              return (T) string;                                //强制转化为T,运行时,编译器会自动识别其类型
       }
        @SuppressWarnings( "unchecked")
        public static <T,U> U getStaticMiddle3(T[] a){
              if( a == null || a. length == 0){
                     return null;
             }
             Integer integer = Integer. valueOf((String) a[1]);
              return (U) integer;                              //返回类型为U,将Integer类型强制转化为U,但在调用此方法时,System.out.println会报错,因为println不知道输入的是什么类型的PrintStream
       }
       @SuppressWarnings( "unchecked")
        public static <T> T getStaticMiddle4(String[] a){
              if( a == null || a. length == 0){
                     return null;
             }
             Integer integer = Integer. valueOf((String) a[1]);
              return (T) integer;
       }
         5).泛型方法,是在调用方法的时候指明泛型的具体类型
             PairFunctin pairFunctin = new PairFunctin();
             System. out.println( pairFunctin.getMiddle( strings).toString());
             System. out.println(PairFunctin. getStaticMiddle( strings));
             System. out.println(PairFunctin. getStaticMiddle1( strings));
             System. out.println(PairFunctin. getStaticMiddle2( strings));
             System. out.println((Integer)PairFunctin. getStaticMiddle3( strings));       //所以,这里调用的时候,还是需要强制转化为Integer
             System. out.println((Integer)PairFunctin. getStaticMiddle4( strings));
结果为:
dfdfd123wueeoerooifwpjsj
dfdfd
123
dfdfd123wueeoerooifwpjsj
123
123
    (4).在泛型类中使用泛型方法
           public class GenericityClass<T> {
            /*
                *注意:
                *   在静态方法中使用泛型类中的参数类型T,会报错,所以,在静态方法中使用类型参数,该静态方法必须为泛型方法
                * 这是因为静态方法无法访问类上定义的泛型
                public static void getStaticMiddle5( T[] a){           // Cannot make a static reference to the non-static type T
                     ......    
               }
             */
                        
               @SuppressWarnings( "unchecked")
                public <U> T getMiddle1(U[] a){      //U与T可以为同一类型,也可以为不同类型
                      if( a == null || a. length == 0){
                         return null;
                     }
                     Integer integer = Integer. valueOf((String) a[1]);
                      return (T) integer;    //U与T不是同一类型,U为String,T为Integer
               }
 
                @SuppressWarnings({ "hiding", "unchecked" })
                 public <T> T getMiddle2(T[] a){
                      if ( a == null || a. length ==0) {
                             return null;
                     }
                     System. out.println( a.getClass().getName());              //结果为:[Ljava.lang.String;,说明自动识别了数组a为String类型
                     T  integer =(T)Integer.valueOf((String) a[1]);
                     System. out.println( integer.getClass().getName());     //结果为:java.lang.Integer
                      return integer; //这里结合PairTest类来讲,泛型方法中的T为String,而GenericityClass泛型类中的T为Integer,说明返回的类型与泛型方法中自动检查到的类型一致
               }
        
                @SuppressWarnings({ "unchecked", "hiding" })
                 public <T,U> U getMiddle3(T[] a){
                      if ( a == null || a. length ==0) {
                             return null;
                     }
                     U  integer =(U)Integer. valueOf((String) a[1]);
                      return  integer;                                        //运行时,JVM无法判别integer为啥类型
                }
         }  
            在PairTest类中:
                GenericityClass<Integer> genericityClass = new GenericityClass<>();
                System. out.println( genericityClass.getMiddle1( strings));
            结果为:
                123
               //System. out.println( genericityClass.getMiddle2( strings));      //在编译时,没有报错,在运行时,报错: java.lang.ClassCastException : java.lang.Integer cannot be cast to java.lang.String
                 System.out.println(String.valueOf(genericityClass.getMiddle2(strings)));   
            结果为:
                123
              //System. out. println( genericityClass.getMiddle3( strings)); 在编译时,就报错: The method println(boolean) is ambiguous for the type PrintStream,即type模糊
                                    System.out.println((Integer)genericityClass.getMiddle3(strings)) ;
                        结果为:
                                123
    
6.类型变量的限定
    有时,需要对泛型类和泛型方法中的类型变量加以约束
            class ArrayAlg{
                public static <T> T min(T[] a){
                    if(a == null || a.length == 0){
                        return null;
                    }
                    T smallest = a[0];                                        
                    for(int i = 0; i < a.length; i++){
                        if(smallest.compareTo(a[i] > 0){               //比如String LocalDate实现了接口Comparable,Rectangle没有实现Comparable
                            smallest = a[i];
                        }
                    }
                    return smallest;
  z     }
在Java中,并不是所有的类都去实现了Comparable接口,调用compareTo方法,所以为了运行时不会报错,该泛型方法需要对其类型变量去进行约束:T extends Comparable,表明只有实现了Comparable接口的类类型,才能使用该泛型方法
即: public static < T extends Comparable> min(T[] a){....}
注意:
这里使用的是extends
(1).表示T应该是绑定类型的子类型,T和绑定类型可以是类,也可以是接口
(2).选择关键字extends的原因是更接近子类的概念
(3).一个类型变量或通配符可以有多个限定  如:T extends Comparable & Serializable,限定类型用“&”分隔,而用逗号来分隔类型变量
(4).在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类,如果用一个类做为限定,它必须是限定列表中的第一个
实例:
public class RestrainGenericityClassOrMethod <T> {
        @SuppressWarnings({ "rawtypes", "unchecked" })
        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 = 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);
       }
}
在PairTest类中:
LocalDate[] birthday = {
                           LocalDate. of(1992, 4, 24),
                           LocalDate. of(1989, 9, 06),
                           LocalDate. of(1988, 2, 11),
                           LocalDate. of(1995, 3, 11),
                           LocalDate. of(1966, 10, 15)
             };
             Pair<LocalDate> pair2 = RestrainGenericityClassOrMethod. minmax( birthday);
             System. out.println( pair2.toString());
结果为:
Pair类中的first的值为:1995-03-11
Pair类中的second的值为:1966-10-15
 
7.类型擦除
    虚拟机没有泛型类型变量---所有对象都属于普通类
    无论何时定义一个泛型类型,都自动提供了一个相应的原始类型,原始类型的名字就是删去类型参数后的泛型类型名,擦除类型变量,并替换为限定类型(无限定类型则用Object)
如:上面的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 void setFirst(Object  first) {
              this. first = first;
       }
        public ObjectT getSecond() {
              return second;
       }
        public void setSecond(Object  second) {
              this. second = second;
       }
        @Override
        public String toString() {
              return "Pair类中的first的值为:" + this.getFirst() + '\n' + "Pair类中的second的值为:" + this.getSecond();
       }
}
    (1).在程序中可以包含不同类型的Pair,例如,Pair<String>或Pair<LocalDate>,而擦除类型后就变成原始的Pair类型
    (2).原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换  public class  Pair<T extends Comparable & Serializable>{.....},则原始类型为:
        public class Pair{
       private Comparable  first;
        private Comparable  second;
 
        public Pair(Comparable  first,Comparable  second){
              this. first = first;
              this. second = second;
       }
        public Comparable getFirst() {
              return first;
       }
            ....
    }
    (3).如果泛型类为:public class Pair<Serializable & Comparable>{...},则原始类型为:
        public class Pair{
       private Serializeable  first;
        private Serializeable  second;
 
        public Pair(Serializeable  first,Serializeable  second){
              this. first = first;
              this. second = second;
       }
        public Serializeable getFirst() {
              return first;
       }
            ....
    }
注意:编译器在必要时要向Comparable插入强制类型转换,为了提供效率,应该将标签接口(没有方法的接口)放在边界列表的末尾
 
8.翻译泛型表达式
    在上面的PairTest类中---Pair类中的参数类型T未有限定,即其原始类型为:T被Object替换
    (1).对于普通方法
            Pair<String> pair = PairFunctin.minMax(strings);
            String first = pair.getFirst();
擦除getFirst()的返回类型后将返回Object类型,编译器会自动插入String的强制类型转换,编译器将把这个方法调用翻译为两条虚拟机指令:
            1).对原始方法pair.getFirst的调用
            2).将返回的Object类型强制转换为String类型
   (2).对于域---假设first与second为public T...
            String s = pair.first;              //编译器也会自动进行强制转换,Object转换为String
 
9.翻译泛型方法
    类型擦除也会出现在泛型方法中
    public static <T extends Comparable> Pair<T> minmax(T[] a){...}
  原类型为:
    public static Pair<Comparable> minmax(Comparable[] a){....}
实例:
     public class GenericMethodErases extends Pair<LocalDate> {     //子类去继承一个泛型类时,泛型类中的参数类型必须指明,要不然,子类必须也为泛型类---这个结果与前面的一样,这里只用普通类继承Pair进行试验
       /*
        * 当父类中有有参构造方法时,子类中也必须有相同的构造函数
        */
        public GenericMethodErases(LocalDate first, LocalDate second) {
              super( first, second);
       }
       
        /* 当父类Pair中没有无参构造方法时,在子类中有无参构造方法,会发生下面的错误:
         * Implicit super constructor Pair<LocalDate>() is undefined. Must explicitly invoke another constructor
         */
        public GenericMethodErases(){}  
        /*
        * 此时的setSecond方法可以看成是Pair类中的setSecond方法的“重载”
        * (non- Javadoc )
        * @see com.genercity.classt.Pair#setSecond(java.lang.Object)
        */
        public void setSecond(LocalDate localDate){          
              if ( localDate.compareTo(getFirst()) <= 0) {                  //当为getFirst()时,调用的是子类中的getFirst方法,如下结果,当为super.getFirst()时,调用的是父类中的getFirst()方法
                    System. out.println( "这是子类中的setSecond方法");
                     super.setSecond( localDate);                  //父类Pair中的set方法
             }
       }
        /*
        * 此时的getFirst方法可以看成是Pair类中的getFirst方法的“重写”
        * (non- Javadoc )
        * @see com.genercity.classt.Pair#getFirst()
        */
       @override
        public LocalDate getFirst() {
             System. out.println( "这是子类中的getFirst方法:");
              return super.getFirst();
       }
}
在PairTest类中:
             System. out.println( "********************");
             LocalDate localDate = LocalDate. of(1992, 4, 24);
             LocalDate localDate1 = LocalDate. of(1988, 2, 11);
             GenericMethodErases gen = new GenericMethodErases( localDate, localDate1);
              gen.setSecond(LocalDate. of(1979,11,12));
             System. out.println( gen.getFirst().toString());
             
             System. out.println( "********************");
             /*
              * 在虚拟机中只有普通的类,没有泛型类,所以当将GenericMethodErases类对象赋给Pair类对象时,存在两个setScond方法:
              * public void setScond(Object second)---父类中,public void setScond(LocalDate local)---子类中
              * 这与继承中重写的条件不符,当去掉@override时,可以将其看成重载,但在《Java核心技术 卷一》中:
              * 希望对setSecond的调用具有多态性,并调用最合适的那个方法,由于Pair引用了GenericMethodErases类对象,所以应该调用GenericMethodErases类中的setScond方法,
              * 问题在于类型擦除与多态发生了冲突,要解决这个问题,就需要编译器在Pair类中生成一个桥方法,(原书写的是在GenericMethodErases类中)
              * public void setSecond(Object second){setSecond((LocalDate)second);}------隐含强制转换
              * 在该书中,将public Object getFirst()---父类中,public LocalDate getFirst()---子类中,不能这样编写Java代码(在这里,具有相同参数类型的两个方法是不合法的)
              * 但在虚拟机中,用参数类型和返回类型确定一个方法。因此,编译器可能产生两个仅返回类型不同的方法字节码,虚拟机能给正确地处理这一情况
              * 
              */
             Pair<LocalDate> gen1 = new GenericMethodErases( localDate, localDate1);
              gen1.setSecond(LocalDate. of(1969,12,12));
             System. out.println( gen1.getFirst().toString());
 
结果为:
********************
这是子类中的getFirst方法:
这是父类中的getFirst方法
这是子类中的setSecond方法
这是父类中的setSecond方法
这是子中的getFirst方法:
这是父类中的getFirst方法
1992-04-24
********************
这是子类中的getFirst方法:
这是父类中的getFirst方法
这是子类中的setSecond方法
这是父类中的setSecond方法
这是子类中的getFirst方法:
这是父类中的getFirst方法
1992-04-24
 
注意:
有关于Java泛型转换的事实:
    1.虚拟机中没有泛型,只有普通的类和方法
      2.所有的类型参数都用它们的限定类型替换
      3.桥方法被合成来保持多态
      4.为保持类型安全性,必要时插入强制类型转换
 
9.约束与局限性----大部分是由类型擦除引起
    (1).不能用基本类型实例化参数类型----因为类型擦除,擦除之后,Pair类含有Object类型的域,而Object不能存储基本类型值
    (2). 运行时类型查询只适用于原始类型
                a instanceOf Pair<String>  -----error
                a instanceOf Pair<T>     -----error
                Pair<String> p = (Pair<String>)a;   -----warning
 
                Pair<String> p1 = new Pair<String>;
                Pair<Integer> p2 = new Pair<Integer>;
                System.out.println(p1.getClass == P2.getClass) ------true,因为类型擦除之后,都变成了Pair<Object>
     (3).  不能创建参数化类型的数组
                Pair<String>[] table = new Pair<String>[10];------error
                    因为类型擦除之后,数组就变成了Pair[] -----Pair[] table = new Pair[10];
                    Object[] o = table;
                    o[0] = "hello" ;      //error----component type is Pair,抛出ArrayStoreException:抛出以表示尝试将错误类型的对象存储到对象数组中,如Object x[] = new String[3]; x[0] = new Integer(0);
                     o[1] = new Pair<String>("123","fjdjf");
                    System.out.println("o[0]为:" + o[0]);   ---------ArrayStoreException,运行时才会出现错误
            注意:不允许创建,但是能够声明类型为Pair<String>[],即不能通过new Pair<String>[10]初始化
        可以声明通配类型的数组,然后进行类型的转换
                Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];-----因为对于通配符的方式,最后取出数据是要做显式的类型转换的
             List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.  
             /*
              * 在这里犯了一个错误:List为接口,不能实例化,但将上面的这句,在类型擦除之后,看成了List[] list = new List[10],是new了10个List类的对象,其实这里只是创了10个空间,即一个数组,包含10个List元素
              * List[] list = new List[10];  //正确的,但是会报一个警告,--- List是原始类型。 应该参数化对泛型类型列表<E>的引用,如: List<String> list = new List[10];
                           *  但是new List[10],会报一个经过---- 类型安全:类型List[]的表达式需要未经检查的转换以符合List<String>[]
              */
             Object o = lsa;   
             Object[] oa = (Object[]) o;   
             List<Integer> li = new ArrayList<Integer>();   
              li.add( new Integer(3));   
              oa[1] = li; // Correct.   
             Integer i = (Integer) lsa[1].get(0); // OK
             System. out.println( i);
结果为:3
注意:如果需要收集参数化类型对象,只有一种安全而有效的方法:使用ArrayList:ArrayList<Pair<String>>
    (4).警告----@SuppressWarnings("unchecked")    @SafeVarargs
    (5).不能实例化类型变量----new T(....),new T[...],T.Class这样的表达式都是不行的
    (6).不能构造泛型数组
    (7).泛型类的静态上下文中类型变量无效---不能在静态域或方法中引用类型变量
    (8).不能抛出或捕获泛型类的实例---即不能抛出也不能捕获泛型类对象,实际上,甚至泛型类扩展Throwable都是不合法的
        public class Problem<T> extends Exception{....}---error
        catch子句中不能使用类型变量----...catch(T e){Logger.global.info(...);}----error
        在异常规范中使用类型变量是允许的---public static < T extends Throwable> void doWork(T t) throws T{
                                            try{
                                                    .....
                                                }catch(Throwable realCause){
                                                    t.initCause(realCause);
                                                    throw t;
                                                }
                                        }
    (9).可以消除对受查异常的检查---Java异常处理的一个基本原则是,必须为所有受查异常提供一个处理器,不过可以利用泛型消除这个限制(详细的细节看《Java核心技术 卷一》)
    (10).注意擦除后的冲突---(详细的细节看《Java核心技术 卷一》)
 
10.泛型类型的继承规则
    1.Manager继承了Employee
        Pair<Manager> p3 = new Pair<Manager>();
        Pair<Employee> p4 = PairFunction.minMax(p3);     //error---因为minMax返回的是Pair<Manager>,而不是Pair<Employee>
            无论S和T是什么关系,通常,Pair<Manager>和Pair<Employee>之间没有什么关系
                Pair<Manager> manager = new Pair<>("ceo","cfo");
                Pair<Employee> employee = manager;  //illegal but suppose it wasn't
                employee.setFirst("lowEmployee");   // 不可能将一个低员工设置成为管理人员
    2.泛型类可以扩展为或实现其他的泛型类---ArrayList<Manager>可以实现List<Manager>接口,而不是去实现一个List<Employee>接口
 
11.通配符类型
    1.子类类型限定------- 可以从泛型对象读取
        Pair<? extends Employee>---表示任何泛型Pair类型,它的类型参数是Employee的子类    
            public static void printBuddies(Pair<Employee> p){
                Employee first = p.getFirst();
                Employee second = p.getSecond();
                System.out.println(first.getName() + "and" + second.getName() + " are buddies";
            }
        }
        在前面提及过,不能将Pair<Manager>传递给这个方法,但现在可以使用通配符类型:
        public static void printBuddies(Pair<? extends Employee p)----Pair<Manager>是Pair<? extends Employee>的子类型    
        Pair<Manager> managerBuddies = new Pair<>("ceo","cfo");
        Pair<? extends Employee> wildcardBuddies = managerBuddies;  //OK
        wildcardBuddies.setFirst("lowEmployee");   //error---这是因为:Pair<? extends Employee>中,
                ? extends Employee getFirst()
                void setFirst(? extends Employee)  //编译器只知道需要每个Employee的子类型,但是不知道什么具体类型,它拒绝传递任何特定的类型, 毕竟?不能用来匹配
        但是,这里的getFirst()方法可以调用
    2.超类型限定------- 可以从泛型对象写入
        Pair<? super Manager> ----这个通配符限制为Manager的所有超类型
        在Pair<? super Manager>中:void setFirst(? super Manager) 和? super Manager getFirst():
            编译器无法知道setFirst方法的具体类型,因为调用这个方法时不能接受类型为:Employee或Object的参数,只能传递Manager类型的对象,或者某个子类型对象
            如果调用getFirst,不能保证返回对象的类型,只能把它赋给一个Object
                public static void minmaxBonus(Manager[] a,Pair<? super Manager> result){
                    if(a.length == 0) return;
                    Manager min = a[0];
                    Manager max = a[0];
                    for(int i =0; i< a.length;i++){
                        if(min.getBonus() > a[i].getBonus()) min = a[i];
                        if(max.getBonus() < a[i].getBonus()) max = a[i];
                    }
                    result.setFirst(min);
                    result.setBonus(max);
                }
超类型限定的另一种应用
    public static <T extends Comparable<? super T>> T min(T[] a){
                ......
        @Override
        public int compareTo(? super T){...}
    }
    3.无限定通配符
        Pair<?>---有如下方法: ? getFirst() 和 void setFirst(?)---getFirst()的返回值只能赋给一个Object,setFirst()方法不能被调用,甚至不能被Object调用
    Pair<?>与Pair类的本质不同在于:可以用任意Object对象调用原始Pair类的setObject方法
    用处:用来测试Pair是否包含一个null引用
            pubic static boolean hasNull(Pair<?> p){
                return p.getFirst() == null || p.getSecond() == null;
            通过将hasNull转换成泛型方法,可以避免使用通配符类型:public static <T> boolean hasNull(Pair<T> p)--但是,带有通配符的版本可读性更强
    4.通配符捕获
        public static <T> void swpHelper(Pair<T> p){
            T t = p.getFirst();
            p.setFirst(p.getSecond());
            p.setSecond(T);
        }
        public static void swap(Pair<?> p){
            swpHelper(p);
        }
swapHelper方法的参数T捕获通配符,它不知道是哪种类型的通配符,但是,这是一个明确的类型,并且<T>swapHelper的定义只有在T指出类型时才有明确的含义
        public static void maxminBonus(Manager[] a,Pair<? super Manager> result){
            minmaxBonus(a,result);
            P.swap(result);
        }
通配符捕获只有在有许多限制的情况下才是合法的。编译器必须能够确信通配符表达的是单个的,确定的类型,如ArrayList<Pair<T>>中的T永远不能捕获ArrayList<Pair<?>>中的通配符
数组列表可以保存两个Pair<?>,分别针对?的不同类型
 
12.反射与泛型---详细的细节看《Java核心技术 卷一》
 
 
            
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值