Java中的泛型

一、泛型类

(1)泛型类的定义语法

class 类名称 <泛型标识,泛型标识,...> {
    private 泛型标识 变量名;
    ......
    }

(2)常用的泛型标识:T、E、K、V

/**
 * @author xujin
 * @createtime 2021-02-14 14:29
 * @description
 * 泛型类的定义
 * <T>泛型标识-类型参数
 * 创建对象的时候指定具体的数据类型
 * 注意:
 * (1)泛型类在创建对象的时候,没有指定类型,将按照Object类型来操作
 * (2)泛型类的类型参数只能是类类型,不能是基本数据类型
 * (3)泛型类型在逻辑上可以看成是多个不同的类型,但其Class类型实际上都是相同类类型
 */

public class Generic<T> {
    private T value;

    public Generic(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }

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

二、泛型类派生子类

(1)子类也是泛型类,子类和父类的泛型类型要一致

class ChildGeneric<T> extends Generic<T>
public class Parent<T> {
    private T value;

    public T getValue() {
        return value;
    }

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

如果不声明父类的泛型,那么父类默认就是Object类型,如下:

public class ChildFirst<T> extends Parent {
    @Override
    public Object getValue() {
        return super.getValue();
    }
}

这是由于如果子类的泛型类型和父类的泛型类型不同的话,在调用子类的构造方法的时候就会报错,因为调用子类的构造方法里面会调用父类的构造方法,正确如下:

public class ChildFirst<T> extends Parent<T> {
    @Override
    public T getValue() {
        return super.getValue();
    }
}

也可以进行如下写法,但是要保证子类的泛型列表中要有一个泛型类型和父类一致,如下:

public class ChildFirst<T,E,K,V> extends Parent<T> {
    @Override
    public T getValue() {
        return super.getValue();
    }
}

(2)子类不是泛型类,父类要明确泛型的数据类型

class ChildGeneric extends Generic<String>
public class ChildSecond extends Parent<Integer> {
    @Override
    public Integer getValue() {
        return super.getValue();
    }
}

三、泛型接口

(1)泛型接口的定义语法

interface 接口名称<泛型标识,泛型标识,...> {
    泛型标识 方法名();
    ......
}

(2)实现类不是泛型类,接口要明确数据类型

(3)实现类也是泛型类,实现类和接口的泛型类型要一致。

四、泛型方法

(1)语法

修饰符 <T,E,...> 返回值类型 方法名(形参数列表){
 方法体...
}

(2)public与返回值中间的泛型列表非常重要,可以理解为声明此方法为泛型方法

(3)只有声明了泛型列表的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法

(4)泛型列表表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T

(5)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

public class GenericMethod {
    public static <E> E genericMethod(E value) {
        return value;
    }
}

(2)泛型方法与可变参数

public <E> void print(E... e) {
    for(E e1:e) {
        System.out.println(e);
    }
}
public static <E> void print(E... e) {
        for (int i = 0; i < e.length; i++) {
            System.out.println(e[i]);
        }
    }

(3)泛型方法总结
1)泛型方法能使方法独立与类而产生变化
2)如果static方法要使用泛型能力,就必须使使其成为泛型方法

五、类型通配符

(1)什么是类型通配符
1)类型通配符一般是使用“?”代替具体的类型实参
2)所以,类型通配符是类型实参。而不是类型形参

public class Box<E> {
    private E value;

    public E getValue() {
        return value;
    }

    public void setValue(E value) {
        this.value = value;
    }
    public static void showBox(Box<?> box) {
        Object value = box.getValue();
    }
}

(2)类型通配符的上限
语法:

/接口<? extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型

public static void showBox(Box<? extends Number> box) {
        Number value = box.getValue();
    }

有类型通配符上限的集合是不能添加元素的
在这里插入图片描述

(3)类型通配符的下限
语法:

/接口<? super 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型

public static void showBox(Box<? super Number> box) {
        Object value = box.getValue();
    }

而有类型通配符下限的集合是能添加元素的,但不能保证数据的正确性
在这里插入图片描述

六、类型擦除

  泛型是Java1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,于泛型相关的信息会被擦除掉,我们称为--类型擦除

(1)无限制类型擦除
在这里插入图片描述

public class Erasure<T> {
    private T value;

    public T getValue() {
        return value;
    }

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

泛型发生在生产字节码文件的时候,我们用反射来看一下字节码文件中value是什么类型的:

public class Test {
    public static void main(String[] args) {
        Erasure<Integer> erasure = new Erasure<>();
        Class<? extends Erasure> erasureClass = erasure.getClass();
        Field[] declaredFields = erasureClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName()+":"+declaredField.getType().getSimpleName());
        }
    }
}

运行结果如下:
在这里插入图片描述
(2)有限制的类型擦除
在这里插入图片描述

public class Erasure<T extends Number> {
    private T value;

    public T getValue() {
        return value;
    }

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

运行结果:
在这里插入图片描述

七、泛型与数组

(1)可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象,这是因为泛型是在编译器做类型擦除,而数组会在整个编译器去持有相应的数据类型:
在这里插入图片描述
不过可以这样创建:
在这里插入图片描述

(2)可以通过java.lang.reflect.Array的newInstance(Class, int)创建(泛型)T[]数组

public class Fruit<T> {
    private T[] array;

    public Fruit(Class<T> clz, int size) {
        array = (T[]) Array.newInstance(clz, size);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值