23.请不要在新代码中使用原生态类型

声明中具有一个或者多个类型参数的类或者接口就是泛型, 泛型类和接口统称为泛型.
Set是个参数化类型, 表示可以包含任何对象类型的一个集合; Set<?>则是一个通配符类型, 表示只能包含某种未知对象类型的一个集合; Set则是一个原生态类型, 它脱离了泛型系统, 前两种是安全的, 最后一种不安全

参数化的类型 List
实际类型参数 String
泛型 List
形式类型参数 E
无限制通配符类型 List<?>
原生态类型 List
有限制类型参数
递归类型限制 >
有限制通配符类型 List<? extends Number>
泛型方法 static List asList(E[] a)
类型令牌 String.class

24.消除非受检警告

编译器警告: 非受检强制转化警告, 非受检方法调用警告, 非受检普通数组创建警告, 以及非受检转换警告等.

  • 尽可能消除每一个非受检警告
  • 如果无法消除警告, 同时可以证明引起警告的代码是类型安全的, 只有在这种情况下, 才可以用一个@SuppressWarning(“unchecked”)注解来禁止这条警告.
  • 应该始终在尽可能小的范围中使用注解.
  • 每当使用注解时, 都要添加一条注释, 说明为什么这么做是安全的.

25. 列表优于数组

数组与泛型相比, 有两个重要的不同点:

  • 数组是斜边的, 泛型是不可变的
  • 数组是具体话的, 因此数组会在运行时才知道并检查他们的元素类型约束; 相比之下, 泛型则是通过擦除来实现的, 因此泛型只在编译时强化他们的类型信息, 并在运行时丢弃他们的元素类型信息

一般来说, 数组和泛型不能很好的混用, 如果你发现自己将他们混合起来使用, 并且得到了编译时错误或者警告, 你的第一反应就应该是用列表代替数组.

26.优先考虑泛型

将类泛型化的第一个步骤是给它的声明添加一个或者多个类型参数, 然后用相应的类型替换所有的Object类型

27.优先考虑泛型方法

28.利用有限制通配符来提升API的灵活性

参数化类型是不可变的. 换句话说, 对于任何两个截然不同的类型Type1和Type2而言, List既不是List的子类型, 也不是它的超类型. 例如List不是List的子类型. Java提供了一种特殊化的参数化类型, 称作又限制的通配符类型, 来处理类似的情况.

PECS, producer-extends consumer-super
如果参数化类型表示一个T生产者, 就使用<? extends T>; 如果它表示一个T消费者, 就是用<? super T>, 且所有的comparable和conparator都是消费者

一般来说, 如果参数类型只在方法声明中出现一次, 就可以用通配符取代它, 如果是无限制的类型参数, 就用无限制的通配符取代它, 如果是有限制的类型参数, 就用有限制的通配符取代它.

29.优先考虑类型安全的异构容器

Class在jdk1.5版本中被泛型化了, 类的类型从字面上来看不再只是简单的Class, 而是Class, 例如String.class属于Class类型. 当一个类的字面文字被用在方法中, 来表达编译时和运行时的类型信息时, 就被称作type token.

示例代码: 构建一个Favorites类, 允许其客户端从任意数量的其他类中, 保存并获取一个”最喜爱”的实例.

  1. /**
  2. * Favorites
  3. *
  4. * @author gavin
  5. * @version 2020/7/18
  6. */
  7. public class Favorites {
  8. private Map<Class<?>, Object> favorites = new HashMap<>();
  9. public <T> void putFavorites(Class<T> type, T instance) {
  10. if (type == null) {
  11. throw new NullPointerException("type is null!");
  12. }
  13. favorites.put(type, instance);
  14. }
  15. public <T> T getFavorites(Class<T> type) {
  16. return type.cast(favorites.get(type));
  17. }
  18. }

Favorites类是类型安全的, 也是异构的: 不像普通的map, 它的所有键都是不同类型的.
Favorites类有两种局限性值得注意, 首先, 以它的原生态形式使用Class对象, 会造成编译时未受检警告
优化如下:

  1. // 如果放入的实例不是type类型的, 会报ClassCastException
  2. favorites.put(type, type.cast(instance));

第二种局限性在于它不能用在不可具体化的类型中.
换句话说, 可以保存String或String[], 但不能保存List, 因为无法为List获得一个Class对象, List和List共用一个List.class对象.
对于第二种局限性, 还没有完全令人满意的解决办法, 有一种方法称作super type token, 在解决这一局限性方面做了很多努力, 但仍有它自身的局限性.