Skip to content

Java

泛型深入

  • 泛型: 是JDK5之后引入的特性, 可以在编译阶段约束操作的数据类型, 并进行检查
  • 泛型的格式: <数据类型>
  • 泛型只能支持引用类型, 如若需要基本类型作为约束, 则需要使用对应的包装类

泛型的好处

  1. 把运行时期的问题提前到了编译期间
  2. 避免了强制类型转换
详细信息

Java中泛型是伪泛型, 只在编译的时候生效, 在编译后生成的class文件是没有泛型的, 这被称为泛型擦除. 泛型的作用就像是一个看门人, 只在开始的时候检查你的类型, 而在(编译)之后会转换为Object类型, 而取出就又会把类型转换为原来的样子. 这么做是为了兼容JDK5之前的版本.

同时也可以理解为什么泛型只支持引用类型, 因为基本类型无法转换为Object类型

泛型的细节

  • 指定泛型的具体类型后, 传递数据时, 可以传入该类类型和其子类类型
  • 如果不写泛型, 则默认为Object类型

泛型类

使用场景: 当一个类中, 某个变量的数据类型不确定时, 就可以定义带有泛型的类

格式: 修饰符 class 类名<类型> { } 范式: public class ArrayList<E> { }

提示

E是不确定的类型, 只有使用ArrayList创建对象的时候才能确定. 此处的E可以理解为一个变量, 但不是用来记录数据的, 而是记录类型的, 可以写成E. T, K, V

  • E是Element的缩写, 表示元素
  • T是Type的缩写, 表示类型
  • K是Key的缩写, 表示键
  • V是Value的缩写, 表示值
java
import java.util.Arrays;

//类这边的泛型E就相当于定义了一个类型变量, 这样的话其他的方法也可以使用这个变量
public class MyArrayList<E> {
    private Object[] elementData = new Object[10];
    private int size;

    public boolean add(E element) {
        elementData[size] = element;
        size++;
        return true;
    }

    public E get(int index) {
        return (E) elementData[index];
    }

    @Override
    public String toString() {
        return Arrays.toString(elementData);
    }
}
java
public class A03_MyArrayListDemo1 {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();

        list.add("a");

        String s = list.get(0);
        System.out.println(s);

        System.out.println(list);
    }
}

泛型方法

  1. 方法中类型不确定时, 可以使用类型后面定义的泛型, 例如: 上面的add方法. ==> 所有方法都能用此泛型
  2. 方法中类型不确定时, 在方法申明上定义自己的泛型 ==> 只有本方法能用

定义方式: 修饰符 <泛型> 返回值类型 方法名(参数类型) { }
范式: public static <E> E get(E e) { }

java
import java.util.ArrayList;

//类名见名知意
public class ListUtil {
    //私有构造方法
    private ListUtil() {
    }

    //    方法定义为静态
    public static <E> void addAll(ArrayList<E> list, E e1, E e2) {
        list.add(e1);
        list.add(e2);
    }
    // ...为扩展运算符, Js也有
    public static <E> void addAll(ArrayList<E> list, E... e) {// 这时的e是一个数组
        for (int i = 0; i < e.length; i++) {
            System.out.println(e[i]);
        }

    }
}
java
ArrayList<String> list1 = new ArrayList<>();
//无论写多少参数都不会报错
ListUtil.addAll(list1, "123", "456", "789", "abc");

泛型接口

格式: interface 接口名<泛型> { } 范式: public interface List<E> { }

如何使用一个带有泛型的接口:

  1. 实现类给出具体类型
  2. 实现类延续泛型, 创建对象时再确定类型
java
//实现类给出具体类型
public class MyArrayList2 implements List<String> {
    @Override
    public boolean add(String s) {// 只可以存储String类型的数据                          
        return false;
    }
    /* 需要重写的方法... */
}
//创建对象时, 就不需要再确定泛型类型了
MyArrayList2 list2 = new MyArrayList2();
java
// 实现类延续泛型, 创建对象时再确定类型
public class MyArrayList2<E> implements List<E> {
    /* 需要重写的方法... */
        @Override
    public boolean add(E e) {// 延续泛型, 只能当MyArrayList2被创建对象时, 才可以确定
        return false;
    }
}
//创建对象时添加泛型
MyArrayList2<String> list2 = new MyArrayList2<>();
list2.add("a");

泛型的继承和通配符

  • 泛型不具备继承性, 但是数据具有继承性
java
public class Demo1 {
    public static void main(String[] args) {
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();
        //数据具有继承性
        list1.add(new Ye());
        list1.add(new Zi());
        list1.add(new Fu());

        method(list1);
        //泛型不具备继承性
        method(list2);
        method(list3);
    }

    public static void method(ArrayList<Ye> list) {
        
    }
}

class Ye { }

class Fu extends Ye { }

class Zi extends Fu { }

如果无法确定类型, 但可以确定该类型是某个父类的子类, 那么就可以使用通配符?

?单独使用时, 表示不确定的类型

java
public class ForNameDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //首先将字节码文件加载到内存当中, 获取字节码文件对象
        Class<?> clazz1 = Class.forName("pojo.Student");

        Class<Student> studentClass = Student.class;
        System.out.println(clazz1 == studentClass);
    }
}
  • ?表示不确定的类型, 它可以进行类型限定
通配符使用说明
? extends A这个不确定的类型是A或A的子类
? super A这个不确定的类型是A或A的父类
java
public class Demo1 {
    public static void main(String[] args) {
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();
        list1.add(new Ye());
        list1.add(new Zi());
        list1.add(new Fu());
        method(list1);
        //不会报错
        method(list2);
        method(list3);

        method1(list2);
        method1(list1);
        method1(list3);
    }
    //这个不确定的类型是Ye或Ye的子类
    public static void method(ArrayList<? extends Ye> list) {

    }
    //这个不确定的类型是Fu或Fu的父类, 即Fu, 和Ye, 如果传入Zi就会报错
    public static void method1(ArrayList<? super Fu> list) {  }
}

class Ye { }

class Fu extends Ye { }

class Zi extends Fu { }

使用场景

  1. 如果类型不确定, 但是能够知道此类型是在某个继承体系下的, 就可以使用泛型的通配符

Released under the MIT License.