# 泛型简介

泛型是 Java SE 1.5 的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。理解 Java 泛型最简单的方法是把它看成一种便捷语法,能节省你某些 Java 类型转换 (casting) 上的操作。

// 有泛型
List<Apple> box = new ArrayList<Apple>();
box.add(new Apple());
Apple apple = box.get(0);
// 没有泛型
Apple apple = (Apple)box.get(0);

在以前的版本中使用泛型类型,需要在声明并赋值的时候,两侧都加上泛型类型。例如:

Map<String, String> myMap = new HashMap<String, String>();

很明显,在声明变量时已经指定了参数类型,在初始化对象时还需要再指定。在 Java 7 得到改进

Map<String, String> myMap = new HashMap<>(); // 注意后面的 "<>"

编译器会根据变量声明时的泛型类型自动推断出实例化 HashMap 时的泛型类型。再次提醒一定要注意 new HashMap 后面的 “<>”,只有加上这个 “<>” 才表示是自动类型推断,否则就是非泛型类型的 HashMap ,并且在使用编译器编译源代码时会给出一个警告提示。

但是 Java SE 7 在创建泛型实例时的类型推断是有限制的:只有构造器的参数化类型在上下文中被显著的声明了,才可以使用类型推断,否则不行。例如:下面的例子在 java 7 无法正确编译。

List<String> list = new ArrayList<>();
// 由于 addAll 期望获得 Collection<? extends String > 类型的参数,因此下面的语句无法通过
list.addAll(new ArrayList<>());

addAll 的函数原型是

boolean addAll(Collection<? extends E> c);
// List 就是继承了 Collection

# Java8 改进

java8 里面泛型的目标类型推断主要 2 个:

  • 支持通过方法上下文推断泛型目标类型。

  • 支持在方法调用链路当中,泛型类型推断传递到最后一个方法。

之前的 Java7构造器上下文。

class List<E> {
   static <Z> List<Z> nil() { ... };
   static <Z> List<Z> cons(Z head, List<Z> tail) { ... };
   E head() { ... }
}

我们可以这么使用

// 通过方法赋值的目标参数来自动推断泛型的类型
List<String> l = List.nil();
// 而不是显示的指定类型
//List<String> l = List.<String>nil();
// 通过前面方法参数类型推断泛型的类型
List.cons(42, List.nil());
// 而不是显示的指定类型
//List.cons(42, List.<Integer>nil());

你记住 Java8 可以从方法上下文获取目标类型即可。之前的 list.addAll(new ArrayList<>());list 这个引用就指明了类型,所以 Java8 编译器可以推断出。

# 参考

Java 全栈知识体系:https://pdai.tech/md/java/java8/java8-type.html

注:网上关于 Java8 类型推断优化好多都一模一样,连吐槽都一样,一点都没有改过,由于它们都大多没写发布时间,我也不知道谁抄谁的。