Skip to content

Java 基础 - 泛型

Java 在 JDK1.5 版本加入的泛型为 ”伪泛型“,即在语法上支持泛型,在编译期执行 ”泛型擦除“,将泛型替换为原始类型

泛型的作用:

  1. 适用于多种数据类型执行相同的代码
  2. 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)

一、泛型的使用

1. 泛型类

java

/**
* @param <T> 此处可以随便写标识符号,T 是 type 的简称
*/
static class Point<T> {
    // var 的类型由T指定,即:由外部指定
    private T var;

    // 返回值的类型由外部决定
    public T getVar() {
        return var;
    }

    // 设置的类型也由外部决定
    public void setVar(T var) {
        this.var = var;
    }
}

/**
* 此处指定了两个泛型类型,即多元泛型
*
* @param <K>
* @param <V>
*/
static class Notepad<K, V> {
    // 此变量的类型由外部决定
    private K key;
    // 此变量的类型由外部决定
    private V value;

    public K getKey() {
        return this.key;
    }

    public V getValue() {
        return this.value;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public void setValue(V value) {
        this.value = value;
    }
}

public static void main(String[] args) {

    // 里面的var类型为String类型
    Point<String> p = new Point<>();
    p.setVar("it");
    System.out.println(p.getVar().length());

    // 多元泛型
    // 定义两个泛型类型的对象, 里面的key为String,value为Integer
    Notepad<String, Integer> t = new Notepad<>();
    t.setKey("汤姆");
    t.setValue(20);
    System.out.print("姓名;" + t.getKey());
    System.out.print(",年龄;" + t.getValue());
}

2. 泛型接口

java
/**
* 在接口上定义泛型
*
* @param <T>
*/
interface Info<T> {
    /**
    * 定义抽象方法,抽象方法的返回值就是泛型类型
    *
    * @return
    */
    T getVar();
}

static class InfoImpl<T> implements Info<T> {
    private T var;

    public InfoImpl(T var) {
        this.setVar(var);
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public T getVar() {
        return this.var;
    }
}

public static void main(String[] args) {
    // 声明接口对象
    // 通过子类实例化对象 
    Info<String> i = new InfoImpl<>("汤姆");
    System.out.println("内容:" + i.getVar());
}

3. 泛型方法

在调用方法时指明具体类型

java
/**
* 
* @param clazz
* @param <T> 定义泛型类型
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
private static <T> T create(Class<T> clazz) throws IllegalAccessException, InstantiationException {
    return clazz.newInstance();
}

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    ArrayList list = create(ArrayList.class);
    System.out.println(list);
}

4. 泛型的上下限

以下代码会报错:

java
class A{}
class B extends A {}

// 如下两个方法不会报错
public static void funA(A a) {
    // ...          
}
public static void funB(B b) {
    funA(b);
    // ...             
}

// 如下funD方法会报错
public static void funC(List<A> listA) {
    // ...          
}
public static void funD(List<B> listB) {
    funC(listB); // Unresolved compilation problem: The method doPrint(List<A>) in the type test is not applicable for the arguments (List<B>)
    // ...             
}
  • <?> 无限制通配符
  • ? extends E extends 声明类型上限,类型可以为 E 或者 E 的子类, 可以使用 & 连接多个类型:? extends E & T
  • ? super E super 声明类型下限,类型可以为 E 或者 E 的超类

实例

java

private  <E extends Comparable<? super E>> E max(List<? extends E> e1) {
    if (e1 == null){
        return null;
    }
    //迭代器返回的元素属于 E 的某个子类型
    Iterator<? extends E> iterator = e1.iterator();
    E result = iterator.next();
    while (iterator.hasNext()){
        E next = iterator.next();
        if (next.compareTo(result) > 0){
            result = next;
        }
    }
    return result;
}

5. 泛型数组

java
List<?>[] list = new ArrayList<?>[10];

二、理解泛型

1. 证明泛型擦除

java
public static void main(String[] args) {

    ArrayList<String> list1 = new ArrayList<String>();
    list1.add("abc");

    ArrayList<Integer> list2 = new ArrayList<Integer>();
    list2.add(123);

    System.out.println(list1.getClass() == list2.getClass()); // true
}
java
public static void main(String[] args) throws Exception {

    ArrayList<Integer> list = new ArrayList<Integer>();

    list.add(1);  //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer

    list.getClass().getMethod("add", Object.class).invoke(list, "asd");

    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}

2. 如何执行泛型擦除

java
public static void main(String[] args) {

    // 不指定泛型的时候
    // 这两个参数都是Integer,所以T为Integer类型
    int i = TestGenericErase.add(1, 2);
    // 这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number
    Number f = TestGenericErase.add(1, 1.2);
    //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object
    Object o = TestGenericErase.add(1, "asd");

    // 指定泛型的时候
    // 指定了Integer,所以只能为Integer类型或者其子类
    int a = TestGenericErase.<Integer>add(1, 2);
    //编译错误,指定了Integer,不能为Float
    int b = TestGenericErase.<Integer>add(1, 2.2);
    // 指定为Number,所以可以为Integer和Float
    Number c = TestGenericErase.<Number>add(1, 2.2);
}

/**
* 这是一个简单的泛型方法
*
* @param x
* @param y
* @param <T>
* @return
*/
public static <T> T add(T x, T y) {
    return y;
}

3. 获取泛型的参数类型

java.lang.reflect.Type 是 Java 中所有类型的公共高级接口, 代表了 Java 中的所有类型. Type 体系中类型的包括:数组类型(GenericArrayType)、参数化类型(ParameterizedType)、类型变量(TypeVariable)、通配符类型(WildcardType)、原始类型(Class)、基本类型(Class), 以上这些类型都实现Type接口

java
static class GenericType<T> {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

public static void main(String[] args) {
    GenericType<String> genericType = new GenericType<String>() {
    };
    Type superclass = genericType.getClass().getGenericSuperclass();
    // getActualTypeArguments 返回确切的泛型参数, 如Map<String, Integer>返回[String, Integer]
    Type type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    // class java.lang.String
    System.out.println(type);
}

参考

https://www.pdai.tech/md/java/basic/java-basic-x-generic.html

Java 泛型学习总结