zoukankan      html  css  js  c++  java
  • java中的泛型类和泛型方法

    1.泛型是什么?

    泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。

    可以在集合框架(Collection framework)中看到泛型的动机。例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象。

    因为 Map.get() 被定义为返回 Object,所以一般必须将 Map.get() 的结果强制类型转换为自己期望的类型,如:

    Map m = new HashMap();
    m.put("key", "blarg");
    String s = (String) m.get("key");

    但是有可能某人已经在该映射中保存了不是 String 的东西,这样的话,上面的代码将会抛出 ClassCastException。

    理想情况下,您可能会得出这样一个观点,即 m 是一个 Map,它将 String 键映射到 String 值。这可以让您消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。

    2.泛型的好处

    Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:

    类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

    Java 程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如“String 列表”或者“String 到 String 的映射”。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作 ClassCastException 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。

    消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

    尽管减少强制类型转换可以降低使用泛型类的代码的罗嗦程度,但是声明泛型变量会带来相应的罗嗦。比较下面两个代码例子。

    该代码不使用泛型:

    List li = new ArrayList();
    li.put(new Integer(3));
    Integer i = (Integer) li.get(0);


    该代码使用泛型:

    List<Integer> li = new ArrayList<Integer>();
    li.put(new Integer(3));
    Integer i = li.get(0);


    在简单的程序中使用一次泛型变量不会降低罗嗦程度。但是对于多次使用泛型变量的大型程序来说,则可以累积起来降低罗嗦程度。

    潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。

    由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型时所写的代码,只是更能确保类型安全而已。

    3.泛型用法的例子

    下面的代码示例展示了 JDK 5.0 中集合框架中的 Map 接口的定义的一部分:


    public interface Map<K, V> {
    public void put(K key, V value);
    public V get(K key);
    }

    注意该接口的两个附加物:

    类型参数 K 和 V 在类级别的规格说明,表示在声明一个 Map 类型的变量时指定的类型的占位符。

    在 get()、put() 和其他方法的方法签名中使用的 K 和 V。

    为了赢得使用泛型的好处,必须在定义或实例化 Map 类型的变量时为 K 和 V 提供具体的值。以一种相对直观的方式做这件事:

    Map<String, String> m = new HashMap<String, String>();
    m.put("key", "blarg");
    String s = m.get("key");

    当使用 Map 的泛型化版本时,您不再需要将 Map.get() 的结果强制类型转换为 String,因为编译器知道 get() 将返回一个 String。

    在使用泛型的版本中并没有减少键盘录入;实际上,比使用强制类型转换的版本需要做更多键入。使用泛型只是带来了附加的类型安全。因为编译器知道关于您将放进 Map 中的键和值的类型的更多信息,所以类型检查从执行时挪到了编译时,这会提高可靠性并加快开发速度。

    4.命名类型参数

    推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 C++ 约定有所不同(参阅 附录 A:与 C++ 模板的比较),并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式,推荐的名称是:

    K —— 键,比如映射的键。 
    V —— 值,比如 List 和 Set 的内容,或者 Map 中的值。 
    E —— 异常类。 
    T —— 泛型。


    5.泛型不是协变的

    关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List<Object> 不是 List<String> 的父类型。

    如果 A 扩展 B,那么 A 的数组也是 B 的数组,并且完全可以在需要 B[] 的地方使用 A[]:

    Integer[] intArray = new Integer[10]; 
    Number[] numberArray = intArray;

    上面的代码是有效的,因为一个 Integer 是 一个 Number,因而一个 Integer 数组是 一个 Number 数组。但是对于泛型来说则不然。下面的代码是无效的:

    List<Integer> intList = new ArrayList<Integer>();
    List<Number> numberList = intList; // invalid

    这样做很多人觉得麻烦,但避免了如下的问题:

    Person[] person = new Student[5];

    person[0] = new Emploee();

    6.类型通配符

    假设您具有该方法:

    void printList(List l) { 
    for (Object o : l) 
        System.out.println(o); 
    }

    上面的代码在 JDK 5.0 上编译通过,但是如果试图用 List<Integer> 调用它,则会得到警告。出现警告是因为,您将泛型(List<Integer>)传递给一个只承诺将它当作 List(所谓的原始类型)的方法,这将破坏使用泛型的类型安全。

    如果试图编写像下面这样的方法,那么将会怎么样?

    void printList(List<Object> l) { 
    for (Object o : l) 
        System.out.println(o); 
    }

    它仍然不会通过编译,因为一个 List<Integer> 不是 一个 List<Object>(正如前一屏 泛型不是协变的 中所学的)。这才真正烦人 —— 现在您的泛型版本还没有普通的非泛型版本有用!

    解决方案是使用类型通配符:

    void printList(List<?> l) { 
    for (Object o : l) 
        System.out.println(o); 
    }

    上面代码中的问号是一个类型通配符。它读作“问号”。List<?> 是任何泛型 List 的父类型,所以您完全可以将 List<Object>、List<Integer> 或 List<List<List<Flutzpah>>> 传递给 printList()。

  • 相关阅读:
    Zookeeper概念学习系列之分布式事务
    序列化 反序列化 输入流 输出流
    dfs常见的配置文件中的value与description(重要)
    [转]SQL Server Reporting Services
    [转]webpack4.0.1安装问题和webpack.config.js的配置变化
    [转]Vue.js 入门教程
    [转]Webpack 入门教程
    [转]使用C#调用cmd来执行sql脚本
    [转]winform利用读取xml获取webconfig
    [转]bootstrap栅格系统的属性及使用
  • 原文地址:https://www.cnblogs.com/toulon/p/4310833.html
Copyright © 2011-2022 走看看