是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,而这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口。
注意:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
好处

  • 避免了类型强转的麻烦。
  • 它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。

使用
泛型虽然通常会被大量的使用在集合当中,但是我们也可以完整的学习泛型只是。泛型有三种使用方式,分别为:泛型类、泛型方法、泛型接口。将数据类型作为参数进行传递。

泛型方法(

泛型方法,是在调用方法的时候指明泛型的具体类型 。
定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }

  1. /**
  2. *
  3. * @param t 传入泛型的参数
  4. * @param <T> 泛型的类型
  5. * @return T 返回值为T类型
  6. * 说明:
  7. * 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
  8. * 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  9. * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  10. * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E等形式的参数常用于表示泛型。
  11. */
  12. public <T> T genercMethod(T t){
  13. System.out.println(t.getClass());
  14. System.out.println(t);
  15. return t;
  16. }

调用方法时

  1. public static void main(String[] args) {
  2. GenericsClassDemo<String> genericString = new GenericsClassDemo("helloGeneric"); //这里的泛型跟下面调用的泛型方法可以不一样。
  3. String str = genericString.genercMethod("hello");//传入的是String类型,返回的也是String类型
  4. Integer i = genericString.genercMethod(123);//传入的是Integer类型,返回的也是Integer类型
  5. }

输出结果:
class java.lang.String
hello
class java.lang.Integer
123
结论:泛型方法随着我们的传入参数类型不同,他得到的类型也不同。泛型方法能使方法独立于类而产生变化。

泛型类

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种集合框架容器类,如:List、Set、Map。
泛型类的定义格式:修饰符 class 类名<代表泛型的变量> { }

  1. /**
  2. * @param <T> 这里解释下<T>中的T:
  3. * 此处的T可以随便写为任意标识,常见的有T、E等形式的参数表示泛型
  4. * 泛型在定义的时候不具体,使用的时候才变得具体。
  5. * 在使用的时候确定泛型的具体数据类型。即在创建对象的时候确定泛型。
  6. */
  7. public class GenericsClassDemo<T> {
  8. //t这个成员变量的类型为T,T的类型由外部指定
  9. private T t;
  10. //泛型构造方法形参t的类型也为T,T的类型由外部指定
  11. public GenericsClassDemo(T t) {
  12. this.t = t;
  13. }
  14. //泛型方法getT的返回值类型为T,T的类型由外部指定
  15. public T getT() {
  16. return t;
  17. }
  18. }

泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。即:在创建对象的时候确定泛型。

泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。
泛型类的定义格式:修饰符 interface接口名<代表泛型的变量> { }

/**
 * 定义一个泛型接口
 */
public interface GenericsInteface<T> {
    public abstract void add(T t); 
}

使用格式:

  1. 定义类时确定泛型的类型

    public class GenericsImp implements GenericsInteface<String> {
     @Override
     public void add(String s) {
         System.out.println("设置了泛型为String类型");
     }
    }
    
  2. 始终不确定泛型的类型,直到创建对象时,确定泛型的类型

    public class GenericsImp<T> implements GenericsInteface<T> {
     @Override
     public void add(T t) {
         System.out.println("没有设置类型");
     }
    }
    

    确定泛型:

    public class GenericsTest {
     public static void main(String[] args) {
         GenericsImp<Integer> gi = new GenericsImp<>();
         gi.add(66);
     }
    }
    

    类型通配符?

    当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

    通配符基本使用

    泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
    此时只能接受数据,不能往该集合中存储数据。 ```java // ?代表可以接收任意类型 // 泛型不存在继承、多态关系,泛型左右两边要一样 //ArrayList list = new ArrayList();这种是错误的 //泛型通配符?:左边写<?> 右边的泛型可以是任意类型 ArrayList<?> list1 = new ArrayList(); ArrayList<?> list2 = new ArrayList(); ArrayList<?> list3 = new ArrayList();

    **注意**:泛型不存在继承、多态关系,泛型左右两边要一样,jdk1.7后右边的泛型可以省略。<br />而泛型通配符?,右边的泛型可以是任意类型。
    ```java
    /**泛型通配符?主要应用在参数传递方面**/
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        test(list1);
        ArrayList<String> list2 = new ArrayList<String>();
        test(list2);
    }
    public static void test(ArrayList<?> coll){
    }
    

    通配符高级使用

    之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
    泛型的上限

    • 格式: 类型名称 <? extends 类 > 对象名称
    • 意义: 只能接收该类型及其子类

    泛型的下限

    • 格式: 类型名称 <? super 类 > 对象名称
    • 意义: 只能接收该类型及其父类型
      //现已知Object类,Animal类,Dog类,Cat类,其中Animal是Dog,Cat的父类
      class Animal{}//父类
      class Dog extends Animal{}//子类
      class Cat extends Animal{}//子类
      
      泛型的上限<? extends 类 >,泛型的上限只能是该类型的类型及其子类. ```java //泛型的上限<? extends 类 >,泛型的上限只能是该类型的类型及其子类. // ArrayList<? extends Animal> list = new ArrayList();//报错
        ArrayList<? extends Animal> list2 = new ArrayList<Animal>();
        ArrayList<? extends Animal> list3 = new ArrayList<Dog>();
        ArrayList<? extends Animal> list4 = new ArrayList<Cat>();
      
      ![image.png](https://cdn.nlark.com/yuque/0/2022/png/12807354/1650361799820-c4ab79a1-70c5-4878-8fa0-b1a76d82b4a5.png#clientId=ucbf38bb9-9400-4&from=paste&height=329&id=ud146394a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=329&originWidth=568&originalType=binary&ratio=1&size=20761&status=done&style=none&taskId=u60862e2d-9f0c-4bb4-9293-1d876ffdfc0&width=568)
      
      **泛型的下限<? super 类 >,泛型的下限只能是该类型的类型及其父类.**
      ```java
      //泛型的下限<? super 类 >,泛型的下限只能是该类型的类型及其父类.
              ArrayList<? super Animal> list5 = new ArrayList<Object>();
              ArrayList<? super Animal> list6 = new ArrayList<Animal>();
      //        ArrayList<? super Animal> list7 = new ArrayList<Dog>();//报错
      //        ArrayList<? super Animal> list8 = new ArrayList<Cat>();//报错
      

      image.png
      一般泛型的上限和下限也是用来参数的传递

      public static void main(String[] args) {
          Collection<Integer> list1 = new ArrayList<Integer>();
          Collection<String> list2 = new ArrayList<String>();
          Collection<Number> list3 = new ArrayList<Number>();
          Collection<Object> list4 = new ArrayList<Object>();
          getElement(list1);
          getElement(list2);//报错
          getElement(list3);
          getElement(list4);//报错
          getElement2(list1);//报错
          getElement2(list2);//报错
          getElement2(list3);
          getElement2(list4);
      }
      // 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
      public static void getElement1(Collection<? extends Number> coll){}
      // 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
      public static void getElement2(Collection<? super Number> coll){}
      

      类型擦除

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