1. (P 327)“菱形”语法:

    ArrayList<String> files = new ArrayList<>();
    // Java 9 扩展了菱形语法的使用范围,例如:现在可以对匿名子类使用菱形语法
    ArrayList<String> passwords = new ArrayList<>() {
    public String get(int n) {
    return super.get(n).replaceAll(".", "*");
    }
    }
  2. (P 328)定义泛型类:

    public class Pair<T, U> {
    ...
    }

    常见的做法是类型变量使用大写字母,而且很简短:

    • E表示集合的元素类型
    • KV分别表示表的的类型
    • TUS表示任意类型
  3. (P 330)定义泛型方法:类型变量放在修饰符的后面,并在返回类型的前面

    class ArrayAlg {
    public static <T> T getMiddle(T... a) {
    ...
    }
    }

    调用泛型方法:

    String middle = ArrayAlg.<String>getMiddle("John", "Q.", "Public");
    // 大多数情况下,可以省略类型参数
    String middle = ArrayAlg.getMiddle("John", "Q.", "Public"); // 编译器将参数的类型与泛型类型T进行匹配,推断出T一定是String
  4. (P 332)类型变量的限定

    T是限定类型(bounding type)的子类型:

    <T extends BoundingType>

    一个类型变量或通配符可以有多个限定,限定类型用“&”分隔,而逗号用来分隔类型变量

    <T extends BoundingType1 & BoundingType2>

    可以拥有多个接口超类型,但最多有一个限定可以是类。如果有一个类作为限定,它必须是限定列表中的第一个限定

  5. (P 333)类型擦除:无论何时定义一个泛型类型,都会自动提供一个相应的原始类型。这个原始类型的名字就是去掉类型参数后的泛型类型名。类型变量会被擦除,并替换为其限定类型(或者,对于无限定的变量则替换为Object)

  6. (P 334)为了提高效率,应该将标签接口(即没有方法的接口)放在限定列表的末尾

  7. (P 335)调用一个泛型方法时,编译器会擦除返回类型,并插入强制类型转换。当访问一个泛型字段时,也会插入强制类型转换。

    Pair<Employee> buddies = ...;
    Employee buddy = buddies.getFirst();
    // 编译器会做如下类似的处理
    Pair buddies = ...; // 擦除类型参数,Pair中的所有泛型被替换为Object
    Employee buddy = (Employee) buddies.getFirst(); // 插入强制类型转换(方法原来的返回类型被擦除变成了Object)
  8. (P 335)桥方法:用来解决多态调用类型擦除的冲突

    方法的擦除会带来两个问题,考虑如下代码:

    class DateInterval extends Pair<LocalDate> {
    // (伪)重写父类中的方法
    // 之所以这里加个“伪”字,是因为父类的类型参数会被编译器擦除,变成Object,所以这里实际上是重载了父类中的方法,只是看起来像重写
    public void setSecond(LocalDate second) {
    ...
    }
    // 这个类中,除了上面的那个外,还存在一个从父类继承的方法
    public void setSecond(Object second);
    }

    这样在多态调用时会产生问题:

    DateInterval interval = ...;
    Pair<LocalDate> pair = interval;
    pair.setSecond(aDate); // 这里调用的是哪个方法呢?类型擦除和多态发生了冲突
    // 如果编译器什么都不做,将调用Pair.setSecond(Object),因为Pair中只存在这一个setSecond方法
    // 而我们希望进行多态调用,即调用DateInterval.setSecond(LocalDate)

    为了解决这个问题,编译器会在子类中生成一个桥方法:

    class DateInterval extends Pair<LocalDate> {
    // (伪)重写父类中的方法
    public void setSecond(LocalDate second) {
    ...
    }
    // 编译器生成的桥方法,重写了父类的setSecond方法
    public void setSecond(Object second) {
    setSecond((LocalDate) second); // 调用上面的那个setSecond方法
    }
    }

    另外,还有一个问题,考虑如下代码:

    class DateInterval extends Pair<LocalDate> {
    // (伪)重写父类中的方法
    public LocalDate getSecond() {
    ...
    }
    // 同理,编译器会生成桥方法,以便进行多态调用
    public Object getSecond() {
    return (LocalDate) getSecond(); // 这里调用的是哪个方法呢?方法重载时要求参数类型不同,但是这里两个getSecond方法都没有参数,似乎不合法
    }
    }

    程序员是不能这样编写Java代码的,但是在虚拟机中,会由参数类型返回类型共同指定一个方法。因此,编译器可以为两个仅返回类型不同的方法生成字节码,虚拟机能够正确地处理这种情况

  9. (P 337)对于Java泛型的转换,有如下几个事实:

    • 虚拟机中没有泛型,只有普通的类和方法
    • 所有的类型参数都会替换为它们的限定类型
    • 会合成桥方法来保持多态
    • 为保持类型安全性,必要时会插入强制类型转换
  10. (P 337)在泛型代码和遗留代码之间进行互操作时,编译器会发出一个警告,可以通过加注解@SuppressWarnings("unchecked")使之消失

    // 将泛型对象赋给原始类型对象
    Dictionary<Integer, Component> labelTable = ...;
    @SuppressWarnings("unchecked") // 抑制编译器的警告
    slider.setLabelTabel(labelTable); // warning // 将原始类型对象赋给泛型对象
    @SuppressWarnings("unchecked") // 抑制编译器的警告
    Dictionary<Integer, Component> labelTable = slider.getLabelTable(); // warning
  11. (P 338)限制与局限性:

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

      Pair<double> pair = ...; // 不合法,double是基本类型
    • 运行时类型查询只适用于原始类型

      if (a instanceof Pair<String>)		// 错误
      if (a instanceof Pair<T>) // 错误
      if (a instanceof Pair) // 正确 Pair<String> pair = (Pair<String>) a; // 错误

      getClass方法总是返回原始类型

      Pair<String> stringPair = ...;
      Pair<Employee> employeePair = ...;
      if (stringPair.getClass() == employeePair.getClass()) // 比较结果为true,两个getClass调用都返回Pair.class
    • 不能创建参数化类型的数组(可以声明,但不能创建)

      var table = new Pair<String>[10];	// 错误
      var table = (Pair<String>[]) new Pair<?>[10]; // 可以,但是结果将是不安全的

      如果需要收集参数化类型对象,简单地使用ArrayList更安全、有效

      var table = new ArrayList<Pair<String>>();	// 合法
    • Varargs警告:向参数个数可变的方法传递一个泛型类型的实例,编译器会发出一个警告,可以使用@SuppressWarnings("unchecked")或者@SafeVarargs注解来抑制这个警告

      @SafeVarargs
      public static <T> void addAll(Collection<T> coll, T... ts) // 调用这个方法时,虚拟机必须要创建T类型的数组ts
      // 这违反了前面的规则,但此时编译器只会发出一个警告
      • 对于任何只需要读取参数数组元素的方法,都可以使用@SafeVarargs注解
      • @SafeVarargs只能用于声明为staticfinalprivate的构造器和方法。
    • 不能实例化类型变量

      public Pair() {
      first = new T(); // 错误
      second = new T(); // 错误
      }

      Java 8之后,最好的解决办法:让调用者提供一个构造器表达式

      public static <T> Pair<T> makePair(Supplier<T> constr) {
      return new Pair<>(constr.get(), constr.get());
      } Pair<String> p = Pair.makePair(String::new);

      传统的解决方法:通过反射调用Constructor.newInstance方法来构造泛型对象

      first = T.class.getConstructor().newInstance();	// 错误,T被擦除为Object
      
      public static <T> Pair<T> makePair(Class<T> cl) {
      try {
      return new Pair<>(cl.getConstructor().newInstance(), cl.getConstructor().newInstance());
      } catch (Exception e) {
      return null;
      }
      } Pair<String> p = Pair.makePair(String.class);
    • 不能构造泛型数组

      public static <T extends Comparable> T[] minmax(T... a) {
      T[] mm = new T[2]; // 错误
      ...
      }
    • 泛型类的静态上下文中类型变量无效:不能在静态字段或方法中引用类型变量

      public class Singleton<T> {
      private static T singleInstance; // 错误
      public static T getSingleInstance() { // 错误
      ...
      }
      }
    • 不能抛出或捕获泛型类的实例

      public class Problem<T> extends Exception { ... }	// 错误,泛型类不能扩展Throwable
      try { ... } catch (T e) { ... } // 错误,catch子句中不能使用类型变量
    • 可以取消对检查型异常的检查

      通过使用泛型类、擦除和@SuppressWarnings注解,我们就能消除Java类型系统的部分基本限制(详见P 343 ~ P 345)

    • 注意擦除后的冲突:例如在类中增加一个equals方法就可能和从Object中继承的equals方法冲突

      倘若两个接口类型是同一接口的不同参数化,一个类或类型变量就不能同时作为这两个接口类型的子类

      class Employee implements Comparable { ... }
      class Manager extends Employee implements Comparable { ... } // 错误
  12. (P 346)具有继承关系的类如果作为泛型类的类型参数,则这些泛型类之间没有继承关系(通配符类型可以解决这个问题),例如EmployeeManager具有继承关系,但是Pair<Employee>Pair<Manager>之间没有继承关系。注意:数组类型Employee[]Manager[]之间具有继承关系

  13. (P 347)总是可以将参数化类型转换为一个原始类型

    var managerBuddies = new Pair<Manager>(...);
    Pair rawBuddies = managerBuddies; // 合法
  14. (P 347)泛型类可以扩展或实现其他的泛型类。如:ArrayList<T>实现了List<T>接口,这意味着ArrayList<Manager>实现了List<Manager>接口

  15. (P 348)通配符:在通配符类型中,允许类型参数发生变化

    Pair<? extends Employee>	// 表示任何泛型Pair类型,它的类型参数是Employee的子类
    // 如Pair<Manager>是Pair<? extends Employee>的子类

    其中的方法如下:

    ? extends Employee getFirst()		// 合法,可以将返回值赋给一个Employee
    void setFirst(? extends Employee) // 这样不可能调用这个方法,它拒绝传递任何特定的类型
  16. (P 349)超类型限定:? super Manager,这个通配符限制为Manager的所有超类型

    void setFirst(? super Manager)	// 合法,可以向方法传递一个Manager对象,或者其子类型的对象
    ? super Manager getFirst() // 不能调用这个方法,它无法确定返回值的类型,只能赋给Object
  17. (P 350)直观地讲,带有超类型限定的通配符允许你写入一个泛型对象,而带有子类型限定的通配符允许你读取一个泛型对象

  18. (P 351)无限定通配符:在编写不需要实际类型的方法时很有用,可读性更好

    ? getFirst()		// 返回值只能赋给Object
    void setFirst(?) // 不能被调用,甚至不能传递Object(原始的Pair类型可以,这是Pair<T>和Pair主要的不同),可以传递null
  19. (P 352)不能在编写代码中使用“?”作为一种类型,必须保存?类型的变量时,可以通过编写辅助方法(泛型方法)解决

  20. (P 353)通配符捕获只有在非常限定的情况下才是合法的,编译器必须能够保证通配符表示单个确定的类型

  21. (P 356)可以使用java.lang.reflect包中的接口Type表述泛型类型的声明,其包含以下子类:

    • Class类,描述具体类型
    • TypeVariable接口,描述类型变量
    • WildcardType接口,描述通配符
    • ParameterizedType接口,描述泛型类或接口类型
    • GenericArrayType接口,描述泛型数组

《Java核心技术(卷1)》笔记:第8章 泛型程序设计的更多相关文章

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

    第12章 泛型程序设计 本章内容: * 为什么要使用泛型程序设计 * 定义简单泛型类 * 泛型方法 * 类型变量的限定 * 泛型代码和虚拟机 * 约束与局限性 * 泛型类型的继承规则 * 通配符类型 ...

  2. java中的数据类型,运算符,字符串,输入输出,控制流,大数值,数组; 《java核心技术卷i》 第三章:java基本程序结构;

    <java核心技术卷i> 第三章:java基本程序结构: 每次看书,去总结的时候,总会发现一些新的东西,这次对于java的数组有了更深的了解: java中的数据类型,运算符,字符串,输入输 ...

  3. Java核心技术卷阅读随笔--第3章【Java 的基本程序设计结构】

    Java 的基本程序设计结构 现在, 假定已经成功地安装了 JDK,并且能够运行第 2 章中给出的示例程序.我们从现在开始将介绍 Java 应用程序设计.本章主要介绍程序设计的基本概念(如数据类型.分 ...

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

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

  5. Java核心技术卷阅读随笔--第4章【对象与类】

    对 象 与 类 4.1 面向对象程序设计概述 面向对象程序设计(简称 OOP) 是当今主流的程序设计范型, 它已经取代了 20 世纪 70 年代的" 结构化" 过程化程序设计开发技 ...

  6. Java核心技术卷阅读随笔--第2章【Java 程序设计环境】

    Java 程序设计环境 本章主要介绍如何安装 Java 开发工具包( JDK ) 以及如何编译和运行不同类型的程序: 控制台程序. 图形化应用程序以及 applet.运行 JDK 工具的方法是在终端窗 ...

  7. Java核心技术卷1Chapter7笔记 图形程序设计

    Swing是指被绘制的用户界面类,AWT是指像事件处理这样的窗口工具箱的底层机制. SWT,JavaFX是可能的代替技术. 创建框架 在Java中,顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架 ...

  8. 《Java核心技术卷I》——第5章 继承

    在C++中,没有提供用于表示抽象类的特殊关键字.只要有一个纯虚函数,这个类就是抽象类. hashCode()方法是定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址. 绝大 ...

  9. 《Java核心技术卷I》——第3章 Java的基本程序设计结构

    byte和short类型主要用于特定的应用场合,例如,底层的文件处理或者需要控制占用存储空间量的大数组. 十六进制数值有一个前缀0x(如0xCAFE),八进制有一个前缀0,如010对应八进制中的8.很 ...

  10. 《Java核心技术·卷Ⅰ:基础知识(原版10》学习笔记 第5章 继承

    <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 目录 <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 5.1 类.超类和子类 5.1 ...

随机推荐

  1. 【Springboot HBase】遇到的一些问题

    想要运行的代码需要在application中运行 使用@Component并实现CommandLineRunner接口.重写方法@Override run( ) @Component public c ...

  2. ASP.NET中LINQ的基本用法

    此Demo只是一个极其简单的LINQ查询Demo 一个类 using System; using System.Collections.Generic; using System.Linq; usin ...

  3. JAVA-蓝桥杯-算法训练-字符串变换

    问题描述 相信经过这个学期的编程训练,大家对于字符串的操作已经掌握的相当熟练了.今天,徐老师想测试一下大家对于字符串操作的掌握情况.徐老师自己定义了1,2,3,4,5这5个参数分别指代不同的5种字符串 ...

  4. Java实现 LeetCode 632 最小区间(又是先序队列,官方给的是堆)

    632. 最小区间 你有 k 个升序排列的整数数组.找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中. 我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a ...

  5. Java实现 LeetCode 553 最优除法(思路问题)

    553. 最优除法 给定一组正整数,相邻的整数之间将会进行浮点除法操作.例如, [2,3,4] -> 2 / 3 / 4 . 但是,你可以在任意位置添加任意数目的括号,来改变算数的优先级.你需要 ...

  6. Java实现蓝桥杯VIP算法训练 奇变的字符串

    试题 算法训练 奇变的字符串 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 将一个字符串的奇数位(首位为第0位)取出,将其顺序弄反,再放回原字符串的原位置上. 如字符串" ...

  7. Java实现 蓝桥杯VIP 算法提高 字符串跳步

    问题描述 给定一个字符串,你需要从第start位开始每隔step位输出字符串对应位置上的字符. 输入格式 第一行一个只包含小写字母的字符串. 第二行两个非负整数start和step,意义见上. 输出格 ...

  8. Java实现 LeetCode 142 环形链表 II(二)

    142. 环形链表 II 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 ...

  9. Java实现字符串的旋转

    1 问题描述 给定一个字符串,要求将字符串前面的若干个字符移到字符串的尾部.例如,将字符串"abcdef"的前3个字符'a'.'b'和'c'移到字符串的尾部,那么原字符串将变成&q ...

  10. Java实现第十届蓝桥杯人物相关性分析

    试题 H: 人物相关性分析 时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分 [问题描述] 小明正在分析一本小说中的人物相关性.他想知道在小说中 Alice 和 Bob 有多少次同 ...