第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. jq实现前端文件上传

    FormData FormData是XMLHttpRequest Level 2 新增的一个接口. 使用FormData可以实现各种文件上传. 使用 // 创建FormData的实例 var form ...

  2. java使用ffmpeg实现上传视频的转码,提取视频的截图等功能

    ffmpeg视频采集功能非常强大,不仅可以采集视频采集卡或USB摄像头的图像,还可以进行屏幕录制,同时还支持以RTP方式将视频流传送给支持RTSP的流媒体服务器,支持直播应用. 1.能支持的格式 ff ...

  3. vmware 挂起后不能恢复

    报错:未能锁定主内存文件,还原虚拟机状态时出错 虚拟机目录下有一个文件夹,xxx.vmem.lck,里面的lck文件是很久以前的,把它删掉重新恢复就可以了.

  4. NOIP2018游记(划掉) 滚粗记

    Day0 早上摸鱼~, 打几个板子就颓废 中午出发, 在火车上颓元气+睡觉. 到了宾馆发现yhx已经帮我们拿了袋子和狗牌,于是上楼欢乐地搓起了六家统, 一直搓到10点钟才回自己房间. 有六家统就有快乐 ...

  5. mysql8 出现1521错误解决方法

    ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER; #修改加密规则 ALTER USER 'ro ...

  6. python3 第二十七章 - 内置函数之str相关

    Python 的字符串常用内建函数如下: 序号 方法及描述 实例 1 capitalize()将字符串的第一个字符转换为大写   2 center(width, fillchar) 返回一个指定的宽度 ...

  7. AX_CreateAndPostInventJournal

    static void CreateAndPostInventJournal(Args _args) { InventJournalTable inventJournalTableLocal; Inv ...

  8. Hibernate中的实体规则、对象状态和进阶-一级缓存

    一.hibernate中的实体规则 1.实体类创建的注意事项 2.主键类型 3.主键生成策略 (1)代理主键 (2)自然主键 二.hibernate中的对象状态 1.对象分为三种状态 2.三种状态的转 ...

  9. 用HTML做登录网页

    <html>  <head> 这里是文档的头部 ... ... ...<title>定义文档标题...</title> </head> &l ...

  10. Effective Java --使类和成员的可访问性最小化

    尽可能地降低可访问性 接口和成员变量访问级别四种访问级别: 私有的(private) --- 只有在生命该成员的顶层类内部才可以访问 包级私有的(package-private) --- 缺省的&qu ...