zoukankan      html  css  js  c++  java
  • java 泛型 窜讲

    一、为什么使用泛型

         复用性:泛型的本质就是参数化类型,因而使用编写的泛型代码可以被许多不同类型的对象所复用。

         安全性:在对类型Object引用的参数操作时,往往需要进行显式的强制类型转换。这种强制类型转换需要在运行时才能被发现是否转换异常,通过引入泛型能将在运行时才能检查类型转换,提前到编译时期就能检查。

    二、自定义泛型

    java中自定义泛型分为三种:泛型类、泛型接口、泛型方法。

    下面使用一个案例演示泛型类、泛型方法,泛型接口类似,所以不再演示。

    // 自定义泛型类
    public class Generic<T>
    {
        private T second;
        public void setSecond(T newValue)
        {
            second = newValue;
        }
        // 自定义泛型方法
        public static <W> void printValue(W obj)
        {
            System.out.println(obj.toString());
        }
       
        @Override
        public String toString()
        {
            return second.toString();
        }
       
        public static void main(String[] args)
        {
            //
            Generic<String> g = new Generic<String>();
            g.setSecond("zhang");
            System.out.println(g);
           
            // 使用泛型方法
            Generic.printValue(45);
            Generic.printValue("hello");
        }
    }

    泛型方法可以在普遍类中定义,也可以在泛型类中定义。

    三、java泛型的特性

    1、擦除

           java中的泛型是伪泛型,因为在编译期间,所有的泛型信息都会被擦除,而只保留原始类型。比如,代码List<Double>和List<String>在经过编译后,都会变成List类型。
           为什么会出现这种情况呢?这跟java的虚拟机有莫大的关系。java虚拟机没有泛型类型对象-----所有对象都属于普通类。由于在java1.5之前没有泛型,那之前没有泛型的代码怎么与有泛型的代码共存了,为了兼容性,java虚拟机采用统一的普通类。
    下面使用代码,体现类型擦除。
    public static void main(String[] args)
    {
        ArrayList<String> arrayList1=new ArrayList<String>(); 
        arrayList1.add("abc"); 
        ArrayList<Double> arrayList2=new ArrayList<Double>(); 
        arrayList2.add(666.666); 
        System.out.println(arrayList1.getClass()==arrayList2.getClass()); 
    }
    输出结果:

    true

    2、补偿

    public static void main(String[] args)
    {
        ArrayList<String> arrayList1=new ArrayList<String>(); 
        arrayList1.add("abc"); 
        String str = arrayList1.get(0);    // 编译正常
        int str2 = arrayList1.get(0);      // 编译报错  
    }
    arrayList1.get(0);
    编译过后,泛型会被擦除,arrayList1.get(0)返回Object类型。但是由于java的补偿机制,因此编译器会自动的插入String的强制类型转换。由于int类型的str2不能接收经过强制转换的String类型,因而编译报错。
     

    四、java泛型的约束

    1、不能用基本类型实例化类型参数
    比如:错误-->Arraylist<double>; 正确-->Arraylist<Double>
    2、运行时类型查询只适用于原始类型
    3、不能创建参数化类型的数组
    4、不能实例化类型变量
    5、泛型类的静态上下文中类型变量无效
    比如:private static T single; // ERROR
    6、不能抛出或捕获泛型类的实例

    五、通配符类型


    1、限定上界通配符

    比如:List<? extends Animal>,表示任何泛型List类型,它的类型参数是Animal类及子类,List<Animal>、List<Cat>、List<Dog>都是List<? extends Animal>子类型。
    public static void main(String[] args)
    {
        List<? extends Animate> animates = new ArrayList<Animate>();         // OK
        List<? extends Animate> animates1 = new ArrayList<Cat>();            // OK
        List<Animate> animates2 = new ArrayList<Animate>();                  // OK
        List<Animate> animates3 = new ArrayList<Cat>();                      // compile-time error       
    }
    其实,我们可以将List<? extends Animate> animates 看做是List<Animate>、List<Cat>等的集合。
    归纳:假如给定的泛型类型为G,两个具体的泛型参数X、Y,当Y是X的子类时(Y extends X)
    • G<? extends Y> 是 G<? extends X>的子类型(如List<? extends Cat> 是 List<? extends Animal>的子类型)。
    • G<X> 是 G<? extends X>的子类型(如List<Animal> 是 List<? extends Animal>的子类型)
    • G<?> 与 G<? extends Object>等同,如List<?> 与List<? extends Objext>等同

    学到这里,可能会遇到一些疑惑的地方,或者说事理解不透的地方,先观察如下两段代码片段,判断一下其是否可行??
    List<? extends Animal> animal = new ArrayList<>();
    animal.add(new Animal());
    animal.add(new Cat());
    上面的两个add操作都不能通过编译。为什么呢?由于List:add(E e)加入泛型变成List<? extends Animal>:add(? extends Animal e),? extends Animal参数类型无法确定,可以是Animal、Cat等,所以为了保护其类型的一致性,因此不允许向list对象中添加任意对象,除了null。
     
    注意:上界限定一般用在:? extends T get()方法上(读数据操作)
     

    2、限定下界通配符

    比如:List<? super Cat>,这个通配符限制为Cat的所有超类型(包括本类)。
    它的归纳方法与上界通配符相似,这里就不啰嗦了。
    看下面代码:
    List<? super Animate> animates = new ArrayList<>();    
    animates.add(new Animate());
    animates.add(new Cat());
    上述代码编译通过,编译器不知道add方法的确切类型,但是可以用任意Animal对象(或子类型对象)。
     
    注意:下界通配符通常用于:set(? extends T>)方法上(写入数据操作)

    3、无限定通配符

    比如:List<?>,当类型不确定时,才使用,该通配符较少使用。
     
    关于上下界限定通配符,建议参考:《编写高质量代码:改善java程序的151个建议》中建议96:不同的场景使用不同的泛型通配符
     
    参考

    1、Java 泛型通配符?解惑

  • 相关阅读:
    PAT 1025. 反转链表 (25)
    PAT 1024. 科学计数法 (20)
    PAT 1076. Forwards on Weibo (30)
    C++——cout输出小数点后指定位数
    PTA 06-图3 六度空间 (30分)
    PTA 06-图2 Saving James Bond
    PTA
    浙大PTA
    浙大PTA
    随机密码生成
  • 原文地址:https://www.cnblogs.com/aoguren/p/5744923.html
Copyright © 2011-2022 走看看