1 简述你对泛型的理解

泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是所操作的数据类型被指定为一个参数。
泛型可以用在如下地方:

  • 泛型方法
    • <? extends T>表示通配符所代表的类型是T类型的子类;<? super T>表示通配符所代表的的类型是 T 类型的超类。
  • 泛型类
    • 声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 泛型接口

    2 泛型标识符含义

  • Collection、Enum:E element 元素,在 Collection 中应用

  • Map: K,V key,value 键值,在 Map 中应用
  • Class: T Type 类型,在 Java 类中应用

    • N - Number(数值类型)
    • ? - 表示不确定的java类型(通用类型)
    • S、U、V - 2nd、3rd、4th types
  • 类型通配符的上限:< ? extends Number > :泛型值接受 Number 及其下层子类类型实现。

  • 类型通配符下限: < ? super Number> : 类型只能接受 Number 及其上层父类类型。

    3 为什么要引入泛型

    引入泛型是为了让多种数据类型执行相同的代码(代码复用)。
    可以看一个简单的例子: ```java package org.example;

import java.util.ArrayList; import java.util.List;

// 泛型简单示例 public class GenericTest1 { public static void main(String[] args) { Show1 show1 = new Show1(); Show2 show2 = new Show2(); // 非泛型方法需要为每个类型都指定一个重载方法 System.out.println(show1.add(1, 2)); System.out.println(show1.add(1.0f, 2.0f)); System.out.println(show1.add(1.0, 2.0));

  1. System.out.println("===============");
  2. // 泛型方法可以复用一个方法,具体参数类型在调用方法的时候确定
  3. System.out.println(show2.add(1, 2));
  4. System.out.println(show2.add(1.0f, 2.0f));
  5. System.out.println(show2.add(1.0, 2.0));
  6. }
  7. // 如果没有使用泛型,那么不同类型的加法,需要若干个重载方法
  8. static class Show1 {
  9. private int add(int a, int b) {
  10. //System.out.println(a + "+" + b + "=" + (a + b));
  11. System.out.println("------int-------");
  12. return a + b;
  13. }
  14. private float add(float a, float b) {
  15. //System.out.println(a + "+" + b + "=" + (a + b));
  16. System.out.println("------float---------");
  17. return a + b;
  18. }
  19. private double add(double a, double b) {
  20. //System.out.println(a + "+" + b + "=" + (a + b));
  21. System.out.println("--------double-------");
  22. return a + b;
  23. }
  24. }
  25. // 通过泛型,可以复用一个方法
  26. static class Show2 {
  27. private <T extends Number> double add(T a, T b) {
  28. System.out.println("----------------");
  29. return a.doubleValue() + b.doubleValue();
  30. }
  31. }

}

  1. 使用了泛型,之后就不需要进行强制类型转换(类型安全,编译器会检查类型)。
  2. ```java
  3. package org.example;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. public class GenericTest2 {
  7. public static void main(String[] args) {
  8. // 不使用泛型,强制转换可能存在问题
  9. List list = new ArrayList();
  10. list.add(123);
  11. list.add("123");
  12. for (int i = 0; i < list.size(); i++) {
  13. String s = (String) list.get(i);
  14. System.out.println(s); //会报错:java.lang.Integer cannot be cast to java.lang.String
  15. }
  16. // 使用泛型指定类型后,非法类型将无法使用
  17. List<String> list2 = new ArrayList<>();
  18. // list2.add(123); // 编译阶段就直接报错
  19. list2.add("123");
  20. }
  21. }

4 泛型擦除

Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的 Java 字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。
比如在代码中定义的 List 和 List 等类型,在编译之后都会变成List 。JVM 看到的只是 List ,而由泛型附加的类型信息对 JVM 来说是不可见的。类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般是 Object 。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。

  1. package org.example;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. // 泛型擦除
  5. public class GenericTest3 {
  6. public static void main(String[] args) {
  7. List<String> stringList = new ArrayList<>();
  8. List<Integer> integerList = new ArrayList<>();
  9. Class<? extends List> stringListClass = stringList.getClass();
  10. Class<? extends List> integerListClass = integerList.getClass();
  11. if (stringListClass.equals(integerListClass)) {
  12. System.out.println("泛型擦除");
  13. }
  14. }
  15. }

5 泛型类、泛型接口、泛型方法

请参考:java 泛型详解

6 参考