zoukankan      html  css  js  c++  java
  • 笔记:泛型

    泛型程序设计意味着编写额代码可以被很多不同类型的对象所重用,并提供强类型校验,避免强制类型转换,并使程序具有更好的可读性和安全性。

    1. 定义简单泛型类

      一个泛型类就是具有一个或多个类型变量的类,定义格式如下:

              public class Pair<T>

              {

                  ……

              }

      泛型类引入了一个类型变量T,使用尖括号括起来,并放在类名的后面,泛型类可以有多个类型变量,多个类型变量使用","号分割,在类定义中的类型变量可以用于指定方法的方法类型、数据域和局部变量的类型,示例如下:

              private T first;

              public void setFirst(T first){…}

              public T getFirst(){…}

      在Java库中,使用类型变量 E 表示集合元素的元素类型;K和V分别表示关键字于值的类型;T、U和S表示任意类型

    2. 定义泛型方法

      定义泛型方法是在普通类中定义的,类型变量放在修饰符的后面,返回类型的前面,定义格式如下:

              public class ArrayAlg{

                  public static <T> T getMiddle(T…a){

                      return a[a.length/2];

                  }

              }

      调用一个泛型方法是,可以在方法名前的尖括号放入具体类型,正常情况下,编译器会推断出具体类型,此时就可以省略,调用格式如下:

      String middle = ArrayAlg.<String>getMiddle("123","456","789");

    3. 类型变量的限定

      有时候类或方法需要对类型变量加以约束,我们可以使用关键字 extends ,定义格式如下:

              public class Pair<T extends Comparable>{

                  ……

              }

      对类型变量T增加了约束,要求类型必须实现了 Comparable 接口,如果传递的类型没有实现这个接口,编译器将产生错误,限定类型可以配置多个,使用"&"分割,实例如下:

      T extends Comparable & Serializable

    4. 泛型类型擦除

      虚拟机没有泛型类型对象,因此无论何时定义一个泛型类型,都自动提供一个相应的原始类型(raw type),原始类型的名字就是删除类型参数后的泛型类型名,擦除类型变量,并替换为限定类型,如果没有限定类型则使用 Object,例如前面的 Pair<T> 类,原始类型如下:

              public class Pair

              {

                  private Object first;

                  public void setFirst(Object first){…}

                  public Object getFirst(){…}

              }

         

      如果增加了限定名,比如 public class Pair<T extends Comparable> 则其原始类型使用第一个限定类型替换,原始类型如下:

              public class Pair

              {

                  private Comparable first;

                  public void setFirst(Comparable first){…}

                  public Comparable getFirst(){…}

              }

      如果有多个限定类型,则编译器会在必要的时候进行强制转换,因此,为了提高效率,应该将没有方法的接口放在后面。

    5. 返回类型的类型擦除

      但程序调用泛型方法时,如果擦除返回类型,编译器会插入强制类型转换,例如如下代码:

              Pair<Employee> pairObj = …;

              Employee empl = pairObj.getFirst();

      编译器把这个方法调用翻译为两条虚拟机指令:

    • 对原始方法 Pair.getFirst 的调用
    • 将返回的 Object 类型强制转换为 Employee 类型。

    Java泛型转换规则总结如下:

    • 虚拟机中没有泛型,只有普通类和方法
    • 所有类型参数都用他们的限定类型替换
    • 桥方法被合成来保持多态
    • 为保持类型安全性,必要时插入强制类型转换。
    • 约束与局限性
      • 不能用基本类型实例化类型参数

        不能用类型参数代替基本类型,因此没有Pair<double> 只有 Pair<Double>,原因是类型擦除,类型擦除后其原始类型为 Pair,类型参数被 Object 替换,而 Object 不能存储 double 值,因此基本类型只能使用独立的类或方法处理。

      • 运行时类型查询只适用于原始类型

        虚拟机中的对象总有一个特定的非泛型类,因此所有类型查询只产生原始类型,原因是类型擦除。

      • 不能创建参数化类型的数组

        不能实例化参数化类型的数组,原因是类型擦除,数组会记住他的元素类型,如果试图存储其他类型的元素,就会抛出 ArrayStoreException 的异常,因此,可以声明类型为 Pair<String>[] 的变量,但不能用 new Pair<String>[10] 初始化这个变量,如果需要使用泛型类型数组,可以使用 ArrayList<Pair<String>> 。

      • 不能实例化类型变量

        不能使用像 new T(…),new T[…] 或 T.class 这样的表达式中的类型变量,但是可以使用反射调用 Class.newInstance 方法来构造泛型对象,示例代码如下:

                    public static <T, U> Pair<T, U> makePair(Class<T> cl, Class<U> cu) {

                        Pair<T, U> pair = new Pair<T, U>();

                        try {

                            pair.setFirst(cl.newInstance());

                            pair.setSecond(cu.newInstance());

                  

                            return pair;

                        } catch (Exception ex) {

                            ex.printStackTrace();

                            return null;

                        }

                    }

      • 泛型类的静态上下文中类型变量无效

        不能在静态域或方法中引用类型变量,因为类型擦除后,是剩下原始类型,因此禁止使用带有类型变量的静态域和方法。

      • 不能抛出或捕获泛型类的实例

        既不能抛出也不能捕获泛型类对象,泛型类扩展 Throwable 都是不合法的,无法通过编译。

        注意类型擦除后的冲突

        当泛型类型被擦除后,会导致方法冲突,例如代码如下:

                    Public class Pair<T>{

                        public boolean equals(T value){…}

                    }

        改泛型方法类型擦除后,与 Object.equals(Object)发生冲突,因此解决方法是重新命名引发错误的方法;泛型规范说明还提到另外一个原则,同一个类或类型变量不能同时成为两个接口类型的子类,而这两个接口是同一个额接口的不同参数化,例如代码如下,该代码是错误的:

                  class Calendar implements Comparable<Calendar>{…}

                  class GregorianCalendar extends Calendar Implements Comparable<GregorianCalendar>{…}

    1. 通配符类型

      带有超类限定的通配符可以向泛型对象写入,带有子类限定的通配符可以从泛型对象读取,说明如下:

    • 通配符的子类限定

      固定类型的泛型类型系统,在某些是否使用并不合适,因此Java系统增加了通配符类型,实例如下:

      Pair<? extends Employee>

      表示任何泛型Pair类型,它的类型参数是 Employee 的子类,如 Pair<Manager>,但不能是 Pair<String>,假设要编写一个打印雇员的方法,代码如下:

      public static void printBuddies(Pair<Employee> p){…}

      不能将 Pair<Manager>传递给这个方法,要解决这个问题可以使用通配符类型,代码如下:

      public static void printBuddies(Pair<? extends Employee> p){…}

      使用子类限定通配符将不可能调用 setter 方法,但使用 getter 方法是不存在问题的。

    • 通配符的超类(基类)限定

      通配符还可以指定超类型限定,如下所示:

      Pair<? super Manager>

      这个通配符限制为 Manager的所有超类型(基类),带有超类型限定的通配符行为,可以为方法提供参数,但不能使用返回值。

       

       

  • 相关阅读:
    创建数据库指定编码格式
    java开发环境配置
    Eclipse 配置工程
    声明式事务管理 的5 种方式
    web容器启动顺序
    2.1 实践篇:使用ping来检测网速
    1.1 mysql安装
    1.2 测试人员与开发人员比例
    1.0 软件测试能力
    1.4 测试各阶段(单元、集成、系统 、Alpha、Beta、验收)
  • 原文地址:https://www.cnblogs.com/li3807/p/6765033.html
Copyright © 2011-2022 走看看