第12章 泛型程序设计

本章内容:
* 为什么要使用泛型程序设计
* 定义简单泛型类
* 泛型方法
* 类型变量的限定
* 泛型代码和虚拟机
* 约束与局限性
* 泛型类型的继承规则
* 通配符类型
* 反射和泛型
  1. 使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。

12.1 为什么要使用泛型程序设计

  1. 泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用。

12.2 定义简单泛型类

  1. 一个泛型类(generic class)就是具有一个或多个类型变量的类。
  2. 类型变量使用大写形式,且比较短,这是很常见的。在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型。T(需要时还可以用临近的字母U和S)表示“任意类型”。
  3. 用具体的类型替换类型变量就可以实例化泛型类型。
  4. 泛型类可看作普通类的工厂。

12.3 泛型方法

  1. 泛型方法的类型变量放在修饰符(如public static)的后面,返回类型的前面。
  2. 泛型方法可以定义在普通类中,也可以定义在泛型类中。
  3. 当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型。在大多数情况下,方法调用中可以省略具体的类型。

12.4 类型变量的限定

  1. <T extends BoundingType>表示T应该是绑定类型的子类型(subtype)。T和绑定类型可以是类,也可以是借口。选择关键字extends的原因是更接近子类的概念。
  2. 一个类型变量或通配符可以有多个限定。限定类型用“&”分隔,而逗号用来分隔类型变量。
  3. 在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。

12.5 泛型代码和虚拟机

  1. 虚拟机没有泛型类型对象-所有对象都属于普通类。在泛型实现的早期版本中,甚至能够将使用泛型的程序编译为在1.0虚拟机上运行的类文件!这个向后兼容性在Java泛型开发的后期被放弃了。
  2. 无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased)类型变量,并替换为限定类型(无限定的变量用Object)。
  3. 原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换。
  4. 为了提高效率,应该将标签(tagging)接口(即没有方法的接口)放在边界列表的末尾。

12.5.1 翻译泛型表达式

  1. 当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。
  2. 当存取一个泛型域时也要插入强制类型转换。

12.5.2 翻译泛型方法

  1. 有关Java泛型转换的事实:

    • 虚拟机中没有泛型,只有普通的类和方法。
    • 所有的类型参数都用它们的限定类型替换。
    • 桥方法被合成来保持多态。
    • 为保持类型安全性,必要时插入强制类型转换。

12.5.3 调用遗留代码

  1. 设计Java泛型类时,主要目标是允许泛型代码和遗留代码之间能够互操作。
  2. @SuppressWarnings(“unchecked”)关闭对代码的检查。

12.6 约束与局限性

12.6.1 不能用基本类型实例化类型参数

  1. 不能用类型参数代替基本类型。

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

  1. 虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始数据。
  2. 无论何时使用instanceof或涉及泛型类型的强制类型转换表达式都会看到一个编译器警告。
  3. getClass方法总是返回原始类型。

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

  1. Pair<String>[] table=new Pair<String>[10];//error
  2. 不允许创建参数化类型的数组,而声明类型为Pair[]的变量仍是合法的。不过不能用new Pair<String>[10]初始化这个变量。
  3. 如果需要收集参数化类型对象,只有一种安全而有效的方法:使用ArrayList:ArrayList<Pair<String>>

12.6.4 Varargs警告

  1. 向参数个数可变的方法传递一个泛型类型的实例:

    public static <T> void addAll(Collection<T> coll,T... ts)
    {
    for(t:ts) coll.add(t);
    }

    只会得到一个警告,而不是错误。
    可以采用两种方法来抑制这个警告。一种方法是为包含addAll调用的方法增加标注@SuppressWarnings(“unchecked”)。或者在Java SE 7中,还可以用@SafeVarargs直接标注addAll方法。
    对于只需要读取参数数组元素的所有方法,都可以使用这个标注。

12.6.5 不能实例化类型变量

  1. 不能使用像new T(…),new T[…]或T.class这样的表达式中的类型变量。
  2. 不能构造泛型数组。
  3. 如果数组仅仅作为一个类的私有实例域,就可以将这个数组声明为Object[],并且在获取元素时进行类型转换。

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

  1. 不要在静态域或方法中引用类型变量。

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

  1. 泛型类扩展Throwable都是不合法的。
  2. catch子句中不能使用类型变量。
  3. 在异常规范中使用类型变量时允许的。
  4. Java异常处理的一个基本原则是,必须为所有已检查异常提供一个处理器。不过可以利用泛型消除这个限制。关键在于以下方法:
    @SuppressWarnings("unchecked")
    public static <T extends Thowable> void throwAs(Throwable e)throws T
    {
    throw (T)e;
    }
  5. 通过使用泛型类、擦除和@SuppressWarnings标注,就能消除Java类型系统的部分基本限制。

12.6.8 注意擦除后的冲突

  1. 泛型规范原则:“要想支持擦除的转换,就需要强制限制一个类或类型变量不能同时成为两个接口类型的子类,而这两个接口是同一个接口的不同参数化。”

12.7 泛型类型的继承规则

  1. 永远可以将参数化类型转换为一个原始类型。
  2. 泛型类可以扩展或实现其他的泛型类。

12.8 通配符类型

  1. 固定的泛型系统使用并不是很好,可以使用“通配符类型”解决。

12.8.1 通配符的超类型限定

  1. 通配符限定与类型变量限定十分类似,但是,还有一个附加的能力,即可以指定一个超类型限定(supertype bound)。super。
  2. 带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。

12.8.2 无限定通配符

  1. 还可以使用无限定通配符,Pair<?>Pair<?>Pair本质的不同在于:可以用文艺Object对象调用原始的Pair类的setObject方法。

12.8.3 通配符捕获

  1. 通配符不是类型变量,因此,不能在编写代码中使用“?”作为一种类型。
  2. 通配符捕获只有在由许多限制的情况下才是合法的。编译器必须能够确信通配符表达的是单个、确定的类型。

12.9 反射和泛型

  1. Class类是泛型的。String.class实际上是一个Class<String>类的对象(事实上,是唯一的对象)。
  2. java.lang.Class 1.0
    • T newInstance() 5.0
      返回默认构造器构造的一个新实例。
    • T cast(Object obj) 5.0
      如果obj为null或有可能转换为类型T,则返回obj;否则抛出BadCastException异常。
    • T[] getEnumConstants() 5.0
      如果T是枚举类型,则返回所有值组成的数组,否则返回null。
    • Class<? super T> getSuperClass() 5.0
      返回这个类的超类。如果T不是一个类或Object类,则返回null。
    • Constructor getConstructor(Class… parameterTypes) 5.0
    • Constructor getDeclaredConstructor(Class… parameterTypes) 5.0
      获得公有的构造器,或带有给定参数类型的构造器。
  3. java.lang.reflect.Constructor 1.1
    • T newInstance(Object… parameters) 5.0
      返回用指定参数构造的新实例。

12.9.1 使用Class参数进行类型匹配

12.9.2 虚拟机中的泛型类型信息

  1. 包含在类文件中,让泛型反射可用的类型信息与旧的虚拟机不兼容。
  2. 为了表达泛型类型声明,Java SE 5.0在java.lang.reflect包中提供了一个新的接口Type。这个接口包含下列子类型:
    • Class类,描述具体类型。
    • TypeVariable接口,描述类型变量(如T extends Comparable<? super T>)。
    • WildcardType接口,描述通配符(如? super T)。
    • ParameterizedType接口,描述泛型类或接口类型(如Comparable<? super T>)。
    • GenericArrayType接口,描述泛型数组(如T[])。
      注意,最后4个子类型时接口,虚拟机将实例化实现这些接口的适当的类。
  3. java.lang.Class 1.0
    • TypeVariable[] getTypeParameters() 5.0
      如果这个类型被声明为泛型类型,则获得泛型类型变量,否则获得一个长度为0的数组。
    • Type getGenaricSuperclass() 5.0
      获得被声明为这一类型的超类的泛型类型;如果这个类型是Object或不是一个类类型(class type),则返回null。
    • Type[] getGenericInterfaces() 5.0
      获得被声明为这个类型的接口的泛型类型(以声明的次序),否则,如果这个类型没有实现接口,返回长度为0的数组。
  4. java.lang.reflect.Method 1.1
    • TypeVariable[] getTypeParameters() 5.0
      如果这个方法被声明为泛型方法,则获得泛型类型变量,否则返回长度为0的数组。
    • Type getGenericReturnType() 5.0
      获得这个方法被声明的泛型返回类型。
    • Type[] getGenericParameterTypes() 5.0
      获得这个方法被声明的泛型参数类型。如果这个方法没有参数,返回长度为0的数组。
  5. Java.lang.reflect.TypeVariable 5.0
    • String getName()
      获得类型变量的名字。
    • Type[] getBounds()
      获得类型变量的子类限定,否则,如果该变量无限定,则返回长度为0的数组。
  6. Java.lang.reflect.WildcardType 5.0
    • Tyep[] getUpperBounds()
      获得这个类型变量的子类(extends)限定,否则,如果没有子类限定,则返回长度为0的数组。
    • Type[] getLowerBounds()
      获得这个类型变量的超类(super)限定,否则,如果没有超类限定,则返回长度为0的数组。
  7. Java.lang.reflect.ParameterizedType 5.0
    • Type getRawType()
      获得这个参数化类型的原始类型。
    • Type[] getActualTypeArguments()
      获得这个参数化类型声明时所使用的类型参数。
    • Type getOwnerType()
      如果是内部类型,则返回其外部类型,如果是一个顶级类型,则返回null。
  8. Java.lang.reflect.GenericArrayType 5.0
    • Type getGenericComponentType()
      获得声明该数组类型的泛型组合类型。

Java核心技术卷一基础知识-第12章-泛型程序设计-读书笔记的更多相关文章

  1. Java核心技术卷一基础知识-第7章-图形程序设计-读书笔记

    第7章 图形程序设计 本章内容: * Swing概述 * 创建框架 * 框架定位 * 在组件中显示信息 * 处理2D图形 * 使用颜色 * 文本使用特殊字体 * 显示图像 本章主要讲述如何编写定义屏幕 ...

  2. Java核心技术卷一基础知识-第14章-多线程-读书笔记

    第 14 章 多线程 本章内容: * 什么是线程 * 中断线程 * 线程状态 * 线程属性 * 同步 * 阻塞队列 * 线程安全的集合 * Collable与Future * 执行器 * 同步器 * ...

  3. Java核心技术卷一基础知识-第11章-异常、断言、日志和调试-读书笔记

    第11章 异常.断言.日志和调试 本章内容: * 处理错误 * 捕获异常 * 使用异常机制的技巧 * 使用断言 * 日志 * 调试技巧 * GUI程序排错技巧 * 使用调试器 11.1 处理错误 如果 ...

  4. Java核心技术卷一基础知识-第8章-事件处理-读书笔记

    第8章 事件处理 本章内容: * 事件处理基础 * 动作 * 鼠标事件 * AWT事件继承层次 8.1 事件处理基础 在AWT所知的事件范围内,完全可以控制事件从事件源(event source)例如 ...

  5. Java核心技术卷一基础知识-第5章-继承-读书笔记

    第5章 继承 本章内容: * 类.超类和子类 * Object:所有类的超类 * 泛型数组列表 * 对象包装器和自动装箱 * 参数数量可变的方法 * 枚举类 * 反射 * 继承设计的技巧 利用继承,人 ...

  6. Java核心技术卷一基础知识-第3章-Java的基本程序设计结构-读书笔记

    第3章 Java的基本程序设计结构 本章内容: 一个简单的Java应用程序 字符串 注释 输入输出 数据类型 控制流 变量 大数值 运算符 数组 本章主要讲述程序设计相关的基本概念(如数据类型.分支以 ...

  7. Java核心技术卷一基础知识-第9章-Swing用户界面组件-读书笔记

    第9章 Swing用户界面组件 本章内容: * Swing与模型-视图-控制器设计模式 * 布局管理概述 * 文本输入 * 选择组件 * 菜单 * 复杂的布局管理 * 对话框 本章将介绍构造功能更加齐 ...

  8. Java核心技术卷一基础知识-第10章-部署应用程序和applet-读书笔记

    第10章 部署应用程序和applet 本章内容: * JAR文件 * Java Web Start * applet * 应用程序首选项存储 10.1 JAR文件 一个JAR文件既可以包含类文件,也可 ...

  9. Java核心技术卷一基础知识-第6章-接口与内部类-读书笔记

    第6章 接口与内部类 本章内容: * 接口 * 对象克隆 * 接口与回调 * 内部类 * 代理 接口技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现(implement)一 ...

随机推荐

  1. kubernetes + istio进行流量管理

    实验目的: 本文介绍如何通过istio实现域名访问k8s部署的nginx服务 前提: 已经安装了kubernetes的服务器 了解 kubernetes 基本命令如何使用 (kubectl creat ...

  2. Spring Cloud的概述(二)

    1.什么是spring cloud? spring cloud,基于spring boot提供了一套微服务的解决方案,包括服务的注册与发现,配置中心,全链路监控,服务网管,负载均衡,熔断等组件,除了基 ...

  3. js 内置对象参考 (Array,String, Math, Data, Number)

    var str = "helloWorld"; var strOne = "helloWorld"; // charAt() 返回在指定位置的字符. var a ...

  4. 微信支付自带的简易log

    using System; using System.Collections.Generic; using System.Web; using System.IO; namespace WxPayAP ...

  5. gperftools对程序进行分析

    gperftools是google出品的一个性能分析工具,相关介绍可见:https://github.com/gperftools/gperftools/wikigperftools性能分析通过抽样方 ...

  6. Java:ConcurrentHashMap是弱一致的

    本文将用到Java内存模型的happens-before偏序关系(下文将简称为hb)以及ConcurrentHashMap的底层模型相关的知识.happens-before相关内容参见:JLS §17 ...

  7. Eclipse 安装 AmaterasUML 插件

    网上很多Eclipse 安装UML插件教程,可能对高版本Eclipse都无法安装成功,本文提供的安装方式,亲测可用. 一.安装GEF插件 1.打开eclipse官网 https://www.eclip ...

  8. python Strip函数和Split函数的用法总结 (python2.0,但用法与3.0是差不多的)

    strip函数原型 声明:s为字符串,rm为要删除的字符序列. 只能删除开头或是结尾的字符或是字符串.不能删除中间的字符或是字符串. s.strip(rm)        删除s字符串中开头.结尾处, ...

  9. 修改云主机windows密码不生效

    Step1:使用文本工具打开插件路径: 路径为:C:\Program Files\Cloudbase Solutions\Cloudbase-Init\Python\Lib\site-packages ...

  10. Git使用(二、分支的创建和上传)

    介绍使用TortoiseGit创建分支并push到gitlab项目库,转载请注明出处. 一.创建一个新的文件夹,把要待编辑的工程从gitlab上pull到该文件夹. 其中URL从gitlab的对应项目 ...