本文讲述泛型的概念、泛型的应用场景,让我们更了解泛型的作用


目录

一、泛型的概念

1、什么是泛型

2、泛型的好处

3、泛型的相关名词

4、类型变量的应用场景

二、泛型的应用场景

1、泛型类与泛型接口

2、泛型方法

3、类型通配符

3.1、为什么需要通配符

3.2、作用

3.3、类型通配符的分类

3.4、上限通配符

3.5、下限通配符

三、可变参数

1、格式

2、可变参数的应用场景

总结


一、泛型的概念

1、什么是泛型

        例如:生产瓶子的厂家,一开始并不知道我们将来会用瓶子装什么,我们什么都可以装,但是有的时候,我们在使用时,想要限定某个瓶子只能用来装什么,这样我们不会装错,而用的时候也可以放心的使用,无需再三思量。我们生活中是在使用这个瓶子时在瓶子上“贴标签”,这样就轻松解决了问题。

        泛型是JDK 5.0引入的特性,允许在定义类、接口或方法时使用类型参数。泛型的核心目的是提高代码复用性,同时增强类型安全性和减少强制类型转换的需要。

例如:java.lang.Comparable接口和java.util.Comparator接口,是用于对象比较大小的规范接口,这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0。但是并不确定是什么类型的对象比较大小,之前的时候只能用Object类型表示,使用时既麻烦又不安全,因此JDK1.5就给它们增加了泛型。

public interface Comparable<T>{
    int compareTo(T o) ;
}

public interface Comparator<T>{
     int compare(T o1, T o2) ;
}

其中<T>就是类型参数,即泛型。

2、泛型的好处

public class GenericInterfaceTest {
    public static void main(String[] args) {
        Object o = new GenericImpl1().show("2");
        System.out.println(((String)o).length());//强转

//        new GenericImpl2().show("2")//报错
        Integer show = new GenericImpl2().show(2);//无需强转
    }
}
interface GenericInterface<T>{
    T show(T t);
}
class GenericImpl1 implements GenericInterface{//无限定类型
    @Override
    public Object show(Object o) {
        return o;
    }
}
class GenericImpl2 implements GenericInterface<Integer>{///限定类型为Integer
    @Override
    public Integer show(Integer integer) {
        return integer;
    }
}
  • 类型安全:编译时检查类型,避免运行时 ClassCastException

  • 代码简洁:无需强制类型转换

  • 示例对比

    • 无泛型:需要 (String) o强制转换

    • 有泛型:直接使用 Integer o,编译器确保类型正确

3、泛型的相关名词

类型变量(Type Variables) 代表未知的数据类型

<T>, <E><K,V>等

参数化类型(Parameterized Types) 已指定类型参数的类/接口 Comparator<String>
泛化的数组类型(GenericArrayType) 代表未知的数组类型 T[ ]
通配符类型(WildcardType) 表示未知类型 Comparator<?>

4、类型变量<T>的应用场景

  • 声明类或接口时,在类名或接口名后面声明类型变量,我们把这样的类或接口称为泛型类或泛型接口

        【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{
    
        }
        【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{
    
        }

        例如:
        public class ArrayList<E>    
        public interface Map<K,V>{
            ....
        }

  • 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明(是<font color='red'>声明</font>不是单纯的使用)了类型变量的方法称为泛型方法

        【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
            //...
        }

        例如:java.util.Arrays类中的
        public static <T> List<T> asList(T... a){
            ....
        }

二、泛型的应用场景

1、泛型类与泛型接口

泛型类格式:

修饰符  class  类名<类型> { }

泛型接口格式:

修饰符  interface  接口名<类型> { }

        自从JDK1.5引入泛型的概念之后,对之前核心类库中的API做了很大的修改,例如:集合框架集中的相关接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等等。

下面以Collection、ArrayList集合以及Iterator迭代器为例演示,泛型类与泛型接口的使用。

import java.util.Arrays;

public class TestRectangle {
    public static void main(String[] args) {
        Rectangle[] arr = new Rectangle[4];
        arr[0] = new Rectangle(6,2);
        arr[1] = new Rectangle(4,3);
        arr[2] = new Rectangle(12,1);
        arr[3] = new Rectangle(5,4);
        System.out.println("排序之前:");
        for (Rectangle rectangle : arr) {
            System.out.println(rectangle);
        }
        Arrays.sort(arr);
        System.out.println("排序之后:");
        for (Rectangle rectangle : arr) {
            System.out.println(rectangle);
        }
    }
}
class Rectangle implements Comparable<Rectangle>{
    private double length;
    private double width;
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public double area(){
        return length * width;
    }

    public double perimeter(){
        return 2 * (length + width);
    }

    @Override
    public String toString() {
        return "Rectangle{" +
                "length=" + length +
                ", width=" + width +
                ",area =" + area() +
                ",perimeter = " + perimeter() +
                '}';
    }

    @Override
    public int compareTo(Rectangle o) {
        int compare = Double.compare(area(), o.area());
        return compare != 0 ? compare : Double.compare(perimeter(),o.perimeter());
    }
}

2、泛型方法

格式:

修饰符  <类型>  返回值类型  方法名(类型  变量名){ }

public class GenericMethodTest {
    public static void main(String[] args) {
        //不使用泛型
        String[] strArr = {"孙行者","者行孙","行者孙"};
        Object e = new GenericMenthod().getElementNoGeneric(strArr,2);
        System.out.println(e);
        System.out.println(((String)e).length());
//        使用泛型
        Integer[] intArr = {2,6,1};
        Integer element = new GenericMenthod().getElement(intArr, 1);
        System.out.println(element);
    }
}
class GenericMenthod{
    /**
     * 返回指定索引位置元素
     */
    public Object getElementNoGeneric(Object[] array,int index){
        return array[index];
    }

    /**
     * 返回指定索引位置元素
     */
    public <T> T getElement(T[] array,int index){
        return array[index];
    }
}

3、类型通配符

3.1、为什么需要通配符

        List<Number> list = new ArrayList<Integer>(); //编译错误:泛型不支持多态

3.2、作用

        为了表示各种泛型List的父类,可以使用类型通配符

3.3、类型通配符的分类
  • 类型通配符:<?>

          List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型

          这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中 

  • 类型上限通配符:<? extends 类型>

          List<? extends Number>:它表示的类型是Number或者其子类型 

  • 类型下限通配符:<? super 类型>

          List<? super Number>:它表示的类型是Number或者其父类型

案例:

public class GenericTest {
    public static void main(String[] args) {
        System.out.println("----------类型通配符:<?>	");
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();
        //list1.add(3);//编译不通过
    }
}
3.4、上限通配符

List<?  extends  Number> list = new ArrayList<Integer>();

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

public class GenericExtendsTest {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<>();
//        list.get(1);//编译通过
//        list.add(1);//编译不通过,泛型代表确定的类型,?代表不确定类型
        List<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        printElement(list1);
    }

    //该方法能接收的参数:Number的子类
    private static void printElement(List<? extends Number> list) {
        for (Number number : list) {
            System.out.println(number);
        }
    }
}
3.5、下限通配符

List<?  super  Number> list = new ArrayList<Integer>();

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

public class GenericSuperTest {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<>();
//        list.add(1);//编译通过
//        Object object = list.get(1);//读不太好用

        List<Integer> list1 = new ArrayList<>();
        addElement(list1,2);
        System.out.println(list1);
        List<? super Integer> list2 = new ArrayList<>();
        addElement1(list2,2);
        System.out.println(list2);
    }

    //list接收的数据类型(>=Integer)
    private static void addElement(List<? super Integer> list, Integer e) {
        list.add(e);
    }
    //list接收数据类型(>=T)
    private static<T> void addElement1(List<? super T> list1, T e) {
        list1.add(e);
    }
}
读取能力 写入能力 主要用途 类型安全
上限通配符<? extends T> ✅ 可安全读取为 T 类型

❌ 基本不能写入
(只能写null)

1. 从集合获取元素处理
2. 只读操作
3. 生产数据供使用

读取安全
下限通配符<? super T>

❌ 只能读取为 Object 类型

✅ 可写入 T 及其子类

1. 向集合添加元素
2. 写入操作
3. 消费数据
写入安全

三、可变参数

1、格式

修饰符  返回值类型  方法名 ( 数据类型...  变量名){ }

注意:

(1)这里的变量其实是一个数组

(2)如果一个方法有多个参数,包含可变参数,可变参数要放在最后

案例:

public class ParamTest {
    public static void main(String[] args) {
        System.out.println(sum());
        System.out.println(sum(10));
        System.out.println(sum(10, 20));
        System.out.println(sum(10, 20, 30));
        System.out.println(sum(10, 20, 30, 40));
        int[] intArr = {10, 20, 30, 40};
        System.out.println(sum(intArr));
    }

    public static int sum(int... a) {
        int sum = 0;
        for (int i : a) {
            sum += i;
        }
        return sum;
    }

    /*public static int sum(int... a, int b) {
        return 0;
    }*/
}

2、可变参数的应用场景

  • Arrays工具类:

          public static List asList(T... a):返回由指定数组支持的固定大小的列表


总结

  • 泛型本质:类型参数化,安全又灵活

  • 通配符原则

    • extends:能取不能存

    • super:能存不能取

    • <?>:只读不写

  • 可变参数:类型后加...,必须在最后

  • PECS原则:Producer Extends, Consumer Super

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐