Java泛型理解

来源:互联网 发布:o2o网络借贷模式的特点 编辑:程序博客网 时间:2024/05/22 12:40
一、Java泛型引入
    java泛型的应用可以提高的代码的复用性,同时泛型提供了类型检查,减少了数据的类型转换,同时保证了类型安全。
    泛型如何保证类型安全:
    
 List list = new ArrayList();      list.add("abc");      list.add(new Integer(1));    //可以通过编译      for (Object object : list) {          System.out.println((String)object);//抛出ClassCastException异常      }

    上面的代码会在运行时抛出ClassCastException,因为它尝试将一个Integer转换为String。
    而从java5开始,Collection的用法:
     
List<String> list = new ArrayList<>();      list.add("abc");      //list.add(new Integer(1));    //编译错误      for (String string : list) {          System.out.println(string);//无需任何强制类型转换      }

    List的创建增加了类型参数String,因此只能向list添加String类型对象,添加其他对象会抛出编译异常;
    foreach循环不需要再添加任何强制类型转换,也就移除了运行时的ClassCastException异常。
    
二、泛型的类与接口
    使用泛型定义自己的类与接口。
    
 public class Gen {          private Object obj;                    public Object getObj() {              return obj;          }                public void setObj(Object obj) {              this.obj = obj;          }                    public static void main(String[] args) {              Gen gen = new Gen();              gen.setObj("abc");              String str = (String) gen.getObj();//类型转换,可能会引起运行时ClassCastException          }      }

      使用泛型来重新定义Gen————使用<>指定泛型参数。
    
 public class Gen<T> {            T obj;                    public T getObj() {                return obj;            }                    public void setObj(T obj) {                this.obj = obj;            }            public static void main(String[] args) {                Gen<String> gen = new Gen<>();                gen.setObj("abc");        //        gen.setObj(10);        //无法通过编译                String str = gen.getObj();    //无需类型转换                //-----------------------------                Gen gen2 = new Gen();//raw type原始类型                gen2.setObj("abc");                gen2.setObj(10);    //可以通过编译,自动装箱将10转化为Integer对象                Integer num = (Integer) gen2.getObj();//使用了强制类型转换            }        }

        main()方法中使用的是泛型类型Gen<String>,不再需要强制类型转换,也就移除了运行时的ClassCastException。
        也定义了一个没有使用泛型类型的gen2,编译器会警告“Gen is a raw type,References to generic type Gen<T> should be parameterized”。
        当不提供泛型类型时,会默认使用Object代替,所以,gen2可以设置String和Integer类型,不过,应该尽量避免这种情况出现,如此,又需要用到强制类型转换,也伴随着运行时的ClassCastException异常。
        
        可以使用@SuppresWarings("rawtypes")来抑制编译器弹出警告。
        
        接口的泛型应用和类的泛型应用很类似,如下:
       
public interface List <E> {             void add(E x);             Iterator<E> iterator();        }                public interface Iterator<E> {             E next();             boolean hasNext();        }

        也可以将此应用到自定义的接口与类当中。还可以使用多个泛型参数来定义接口与类,比如Map<K,V>.
        泛型类型也可以作为一个参数来用,如:new HashMap<String, List<String>>()。
        
三、泛型的命名规范
        为了与Java关键字区别开,Java泛型参数只是使用一个大写字母来定义。各种常用泛型参数的意义如下:
        E——Element,常用在Java Collection里,如:List<E>,Iterator<E>,Set<E>
        K,V——Key,value,代表Map的键值对
        N——Number,数字
        T——Type,类型,如String,Integer等
        S,U,V——2nd,3rd,4th类型,和T的用法一样
        
四、泛型的方法与构造函数
    如果不希望整个类都被泛型化,可以只在某个方法上应用泛型。还可以在构造函数上应用泛型。
       
public class GenMethod {                public static <T> void fromArrayToCollection(T[] a,Collection<T> c){                    for (T t : a) {                      c.add(t);                    }                }                                public static void main(String[] args) {                    Object[] oa = new Object[100];                    Collection<Object> co = new ArrayList<>();                                        GenMethod.<Object>fromArrayToCollection(oa, co);                }                    }

    定义方法所用的泛型参数需要在修饰符之后添加,如public static <T>,如果有多个泛型参数,可如此定义<K,V>或者<T1,T2>
    不建议在泛型变量里添加其他类型,如下会引起编译错误(或隐含错误):
      
 public static <T> void fromArrayToCollection(T[] a,Collection<T> c){            for (T t : a) {                c.add(t);                c.add(new Object());                }        }

    泛型方法的调用GenMethod.<Object>fromArrayToCollection(oa, co); 在方法前声明了泛型类型Object。
    因为编译器可以推断这个泛型类型,因此也可以这样写: GenMethod.fromArrayToCollection(oa, co)。
    也可以如下:
      
 String[] sa = new String[100];        Collection<String> cs = new ArrayList<String>();                // T 推断为String        fromArrayToCollection(sa, cs);                // T 推断为Object        fromArrayToCollection(sa, co);                Integer[] ia = new Integer[100];        Float[] fa = new Float[100];        Number[] na = new Number[100];        Collection<Number> cn = new ArrayList<Number>();                //T 推断为Number        fromArrayToCollection(ia, cn);                //T 推断为Number        fromArrayToCollection(fa, cn);                //T 推断为Number        fromArrayToCollection(na, cn);                //T 推断为Object        fromArrayToCollection(na, co);                //编译错误,Number与String不能兼容        fromArrayToCollection(na, cs);

        
五、泛型参数的界限
        如果希望泛型类型只能是一部分类型,比如操作数据的时候,会希望是Number或其子类类型,
            这其实就是给泛型参数添加一个界限。其定义形式为:
            <T extends BoundingType>
        此定义表示T应该是BoundingType的子类型(subtype)。T和BoundingType可以是类,也可以是接口。
        而且,此处的“extends”表示的是子类型,不等同于继承。如:
          
 public class Box<T> {                private T t;                            public void set(T t) {                    this.t = t;                }                            public T get() {                    return t;                }                            public <U extends Number> void inspect(U u) {                    System.out.println("T: " + t.getClass().getName());                    System.out.println("U: " + u.getClass().getName());                }                            public static void main(String[] args) {                    Box<String> integerBox = new Box<>();                    integerBox.set("abc");    //能通过编译,因为T指定为String类型            //        integerBox.inspect("abc");//不能通过编译,因为U必须是Number类型或其子类                    integerBox.inspect(new Integer(10));                }            }

            限定了泛型参数的界限,也可以调用BoundingType(如Number)的相对应的方法,如下:
               
public class NumberTest<T extends Integer> {                    private T num;                                        public NumberTest(T num) { this.num = num;}                                        public boolean isOdd(){                        return num.intValue()%2 == 1;                    }                                        //....                }
              
            如何为泛型参数添加多个限制范围,多重限制范围格式如下:
                <T extends A & B & C>
            一个泛型参数可以有多重限制范围,使用“&”分隔。且限制范围中至多有一个类。
            如果用一个类作为限定,它必须是限定列表中的第一个。如:
               
Class A { /* ... */ }                interface B { /* ... */ }                interface C { /* ... */ }                class D <T extends A & B & C> { /* ... */ }

            如果BoundingType不是放在第一位,会产生编译异常:
            
   class D <T extends B & A & C> { /* ... */ }  // 无法通过编译

              
    六、泛型方法与泛参界限的综合
            泛型方法是一个有用的工具,那泛参的界限就是这个工具的灵魂,为这个工具添加了一些“行为准则”。
            如下:设计一个方法,统计在一个数组里比指定元素大的个数,
              
 public static <T> int countGreater(T[] array,T elem) {                    int count = 0;                    for (T t : array) {                        if (t > elem) {//编译错误                            ++count;                        }                    }                    return  count;                }

            上面这个方法无法通过编译,因为操作符“>”只可以用在基本数据类型(byte,char,short,int,float,long,double,boolean),
            不可以用来比较对象之间的大小(除非实现了Comparable接口)。
            所以要为<T>添加一个界限Comparable<T>:
              
 public interface Comparable<T> {                    public int compareTo(T o);                }

            更改后的代码如下:
              
 public static <T extends Comparable<T>> int countGreater(T[] array,T elem) {                    int count = 0;                    for (T t : array) {                        if (t.compareTo(elem) > 0) {//无编译错误                            ++count;                        }                    }                    return  count;                }

            也可以选择添加界限Comparator<T,T>,只不过此界限需要两个参数。
            
七、泛型、继承与子类型
        如果两个类之间相互兼容(继承与被继承),那么便可以将一个类对象赋值给另一个对象,
        比如:可以将一个String对象赋值给Object,String是Object的子类,
           
String someString = new String();Object someObject = new Object();someObject = someString;

        在面向对象中,这是一种“is-a”关系。String是Object的一种对象,所以上面的赋值是可以的。
        这种“is-a”关系,同样也适用泛型。如果将泛参设置Number,那么在随后的调用里,只需要传入一个数据对象就行了,如下:
           
Box<Number> box = new Box<>();box.add(new Integer(1));   box.add(new Double(1.0));

        但是对于这种方法:public void someMethod(Box<Number> n) { /*.....*/}
        可以接受Box<Number>类型的参数,但是不能接受Box<Integer>或者Box<Double>类型的参数。
        因为Box<Integer>和Box<Double>都不是Box<Number>的子类。
        Integer是Number的子类,Box<Integer>和Box<Double>的共同父类是Object,但是它俩之间不是子类关系。
        即,无论类A与类B是否存在关联,myClass<A>和myClass<B>都没有任何关联,其共同的父类是Object。
        
八、泛型类与子类型
         泛型的extends与继承的extends不同,泛型的extends其后可以是一个类,也可以是一个接口。
         泛型的extends代表子类型,而不是子类,或许可以把其等同于extends(继承)和implement的并集。
         
         泛型里,也存在子类型,前提是其泛型参数的限制没有改变,可以认为泛参没有改变,其实就是从原来的类或接口来判断泛型的子类型。
         例如:ArrayList<E> implement List<E>,而List<E> extends  Collection<E>
         那么,ArrayList<String>就是List<String>的子类型,而List<String>则是Collection<String>的子类型。
         
         假设需要定义自己的List接口——PayLoadList,其定义如下:
            interface PayloadList<E,P> extends List<E> {              void setPayload(int index, P val);              //...            }

        则这几种都是List<String>的子类型:
            PayloadList<String,String>            PayloadList<String,Integer>            PayloadList<String,Exception>



















0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 六个月的婴儿消化不好怎么办 六个月婴儿不好好吃奶怎么办 六个月婴儿消化不好怎么办 六个月婴儿肠胃不好怎么办 受了惊吓后睡眠不好怎么办 宝宝夏季吃饭不爱带围嘴怎么办 3岁宝宝爱看电视怎么办 新生儿晚上哭闹不睡觉怎么办 档案丢了怎么办失业证 三岁宝宝不合群怎么办 阴唇手术半月了疤痕增生怎么办 宝宝小阴唇黏连怎么办 6个月宝宝阴唇粘连怎么办? 高一孩子不爱上学怎么办 3岁宝宝外阴发红怎么办 40爸妈离婚我该怎么办 小儿吃糖卡住了怎么办 额头撞墙上肿了怎么办 宝宝额头撞肿了怎么办 小孩额头撞肿了怎么办 宝宝撞到额头肿了怎么办 小孩子上一年级语文很差了怎么办? 楼下说小孩太吵怎么办 托班的小孩太吵怎么办 宝宝两岁只会简单的词怎么办 我儿子的视力低怎么办 小孩子课文看书都不会读怎么办 宝宝衣服买小了怎么办 拉拉裤腰围小了怎么办 一周九个月宝宝发烧怎么办 8个月的婴儿37.7怎么办 宝宝发烧37度3怎么办 2岁宝宝发烧37度怎么办 3个月新生儿发烧怎么办 生完孩子肚子松弛怎么办 6个月婴儿高烧怎么办 7个月婴儿高烧怎么办 11个月婴儿高烧怎么办 4个月婴儿高烧怎么办 新生宝宝混合喂养消化不良怎么办 四个月的宝宝不追听不追视怎么办