Java中的泛型

发布于:2024-04-14 ⋅ 阅读:(145) ⋅ 点赞:(0)
  • 泛型的出现能够使程序员在创建对象的时候指定类中某些成员的类型,而不用在写类时指定。
  • 其次,泛型仅存在在编译阶段,能够在编译阶段检测出代码中的类型错误,比如声明了List<String>,如果add了Integer的1,则编译器就会报错。
  • 如果声明了泛型,但创建时不指定泛型,默认为Object类型。
  • 最后,虽然同一个类不同泛型的对象,本质上是同一个类。
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    List<String> list2 = new ArrayList<>();
    System.out.println(list.getClass() == list2.getClass());
}

在这里插入图片描述
    list声明了Integer,而list2声明了String,但是本质上是同一类,即ArrayList,list.getClass()==list2.getClass()的输出结果为true。

类和接口的泛型
  • 形式为:class 类名|接口名<泛型变量>{},常用的泛型变量可以是T,E,K,V等。

    先来具体看一个比较简单的类泛型例子。

class Animals<T>{
    private T data;
    public Animals(T data){
        this.data = data;
    }
    public T getData() {
        return data;
    }
}
public static void main(String[] args) {
    Animals<Integer> animals = new Animals<>(1);
    System.out.println(animals.getData());
    Animals<String> animals2 = new Animals<>("data");
    System.out.println(animals2.getData());
}

在这里插入图片描述

  • 如果子类或者实现类没有声明泛型,那么父类或者接口必须指出泛型中具体的类型。
class Animals<T>{

}
// Animals中不能为T,必须为某个具体的类型
class Cats extends Animals<Integer>{
    
}
  • 如果子类或者实现类声明的泛型,那么父类或者接口必须和子类或者实现类的泛型保持一致。
interface Animals<T>{
    
}
// 接口中泛型T必须和Cats的泛型T相同,实现类可以自定其他的泛型,比如E
class Cats<T, E> implements Animals<T>{
    
}
方法的泛型
  • 形式为:方法修饰符 <泛型变量> 返回值类型 方法名(参数列表)在<>中声明的泛型变量后,在返回值类型以及参数列表中就可以使用该类型。
  • 方法的泛型独立于类的泛型存在。
  • 声明了泛型的方法可以为静态类,而成员方法中有类声明的泛型时,该方法不能为静态方法。
class Animals<T>{
    private T data;
    public Animals(T data){
        this.data = data;
    }
    // 该方法使用了类中声明的T泛型,因此无法使用static修饰
    public T getData() {
        return data;
    }
    // 虽然这里使用了和Animals类同样的泛型,但是泛型类型不一定相同,泛型方法独立于类泛型
    // 并且该方法可以使用static修饰
    public <T> void print(List<T> dataList){
        for (T t : dataList) {
            System.out.println(t);
        }
    }
}
public static void main(String[] args) {
    Animals<Integer> animals = new Animals<>(1);
    List<String> strLists = new ArrayList<>();
    strLists.add("a");
    strLists.add("b");
    strLists.add("c");
    animals.print(strLists);
}

    上述例子,可以观察到Animals类的泛型是Integer,但是List数组的泛型可以是String类型,因此,泛型方法的泛型是独立于类的泛型的。

类型通配符
  • 类型通配符为?,用于代表某个类型。
  • <? extends T>表示?这个类型必须是T类型或者是T类型的子类。不仅类型通配符如此,泛型也可以<E extends T>表示类型E必须是T类型或其子类。
  • <? super T>表示?这个类型必须是T类型或者是T类型的父类。注意,泛型不可以<E super T>
class Animals<T>{

    private T data;

    public Animals(T data){
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void print(List<? extends T> list){
        // 声明的通配符或者泛型的list无法通过add方法添加元素。
        // list.add(1);
        for (T t : list) {
            System.out.println(t);
        }
    }

}
public static void main(String[] args) {
    Animals<Number> animals = new Animals<>(1);
    List<Integer> list = new ArrayList<>();
    list.add(1);
    animals.print(list);
    List<Number> list2 = new ArrayList<>();
    list2.add(1);
    animals.print(list2);
}

    在上述例子中,在print方法中List集合使用了类型通配符,但是要求是T类型或者是T类型的子类,Animals泛型指定为Number类型,那么要求List集合中存放的元素类型必须是Number类型或者Number类型的子类,Integer类型就是Number类型的子类。

类型擦除

    泛型仅停留在编译阶段,进行JVM之前,泛型会被擦除。

  • 无限制类的泛型擦除,类型擦除后,T类型使用Object类型代替。
    在这里插入图片描述
class Animals<T>{

    private T data;

    public Animals(T data){
        this.data = data;
    }

    public T getData() {
        return data;
    }
    
}
public static void main(String[] args) {
    Animals<Integer> animals = new Animals<>(1);
    Class<? extends Animals> clz = animals.getClass();
    Field[] Fields = clz.getDeclaredFields();
    for (Field field : Fields) {
        System.out.println(field.getName() + ":" + field.getType().getSimpleName());
    }
}

在这里插入图片描述

  • 有限制类的泛型擦除,表示
    在这里插入图片描述
class Animals<T extends Number>{

    private T data;

    public Animals(T data){
        this.data = data;
    }

    public T getData() {
        return data;
    }

}
public static void main(String[] args) {
    Animals<Integer> animals = new Animals<>(1);
    Class<? extends Animals> clz = animals.getClass();
    Field[] Fields = clz.getDeclaredFields();
    for (Field field : Fields) {
        System.out.println(field.getName() + ":" + field.getType().getSimpleName());
    }
}

在这里插入图片描述

  • 有限制方法泛型的擦除
    在这里插入图片描述
  • 无限制接口泛型的擦除,与类的泛型擦除不同,接口中会生成一个桥接方法。
    在这里插入图片描述
interface Info<T>{
    T getInfo();
}

class InfoImpl implements Info<Integer>{

    @Override
    public Integer getInfo() {
        return 0;
    }
}
public static void main(String[] args) {
    InfoImpl info = new InfoImpl();
    Class<? extends InfoImpl> clz = info.getClass();
    Method[] methods = clz.getDeclaredMethods();
    for (Method method : methods) {
        System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
    }
}

在这里插入图片描述
参考:https://www.bilibili.com/video/BV1xJ411n77R?p=1&vd_source=3b276afc1d517ec28a00ae341f72050c


网站公告

今日签到

点亮在社区的每一天
去签到