Java泛型这个特性是从JDK 1.5 才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是再殡仪截断会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。

为什么会引入泛型

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是在泛型的使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称谓泛型类、泛型接口、泛型方法。

引入泛型的意义:

  • 适用于多种数据类型执行相同的代码(代码复用

当没有泛型时,想要实现不同类型的加法,就需要对每种类型都要重载一个add方法如下:

  1. public class GenericsExampleReuse {
  2. private static int add(int a, int b) {
  3. System.out.println(a + "+" + b + "=" + (a+b));
  4. return a + b;
  5. }
  6. private static float add(float a, float b) {
  7. System.out.println(a + "+" + b + "=" + (a+b));
  8. return a + b;
  9. }
  10. private static double add(double a, double b) {
  11. System.out.println(a + "+" + b + "=" + (a+b));
  12. return a + b;
  13. }
  14. }

如果使用泛型,可以复用为同一个方法:

  1. public class GenericsExampleReuse {
  2. private static <T extends Number> double add(T a, T b) {
  3. System.out.println(a + "+" + b + "=" + (a.doubleValue()+b.doubleValue()));
  4. return a.doubleValue() + b.doubleValue();
  5. }
  6. }
  • 泛型中的类型在使用时指定,不需要强制类型转化(类型安全,编译器会检查类型

如下示例: list 中的元素都是Object类型(无法约束其中的类型),所以在取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现 java.lang.ClassCastException 异常。

  1. List list = new ArrayList();
  2. list.add("xxString");
  3. list.add(100d);
  4. list.add(new Person());

引入泛型,可以提供类型约束,提供编译前的检查
image.png

泛型的基本使用

通过一些例子学习泛型的使用;泛型有三种使用方式,分别是:泛型类、泛型接口、泛型方法。参考《李兴华 - Java实战经典》

泛型类

  • 简单的泛型类
    image.png
  • 多元泛型
    image.png

    泛型接口

  • 简单的泛型接口
    image.png

    泛型方法

    泛型方法,是在调用方法的时候指明泛型的具体类型。

  • 定义泛型方法的语法格式
    image.png

  • 调用泛型方法的语法格式
    image.png

定义泛型方法时,必须在返回值前加一个 ,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
Class 的作用就是指明泛型的具体类型,而Class类型的变量c,可以用来创建泛型类的对象。
为什么要用变量c 来创建对象?泛型方法中无法得知具体的类型,也不知道构造方法,所以无法通过new 来创建对象,因此利用变量c 的newInstance 方法去创建对象,即利用反射创建对象
泛型方法要求参数是Class类型,而Class.forName()方法的返回值也是Class,因此可以用Class.forName() 作为参数。其中forName() 方法中的参数是什么类型,返回的Class就是何种类型。
为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。

泛型的上下限

  • 先看下如下的代码,很明显是会报错的 (具体错误原因请参考后文)。 ```java class A{} class B extends A {}

// 如下两个方法不会报错 public static void funA(A a) { // …
} public static void funB(B b) { funA(b); // …
}

// 如下funD方法会报错 public static void funC(List listA) { // …
} public static void funD(List listB) { funC(listB); // Unresolved compilation problem: The method doPrint(List
) in the type test is not applicable for the arguments (List) // …
} ``` 那么如何解决?
为了解决泛型中隐含的转换问题,Java泛型引入了类型参数的上下边界机制。<? extends A> 表示该类型参数可以是A(上边界)或者A的子类类型。编译时擦除到类型A,即用A类型代替类型参数。这种方法可以解决开始