Java 泛型

什么是泛型?为什么需要泛型?

泛型的定义

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型

泛型的场景意义

也就是说泛型是一个提供编译器提前检测的机制,可以从一个例子看出

public static void main(String[] args){
	List list = new ArrayList();
	list.add("1");
	list.add(2);
	
	for(int i = 0; i< arrayList.size();i++){
	    String item = (String)arrayList.get(i);
	}
}

在执行过后,会抛出异常

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

缺少了一个检测的机制,就需要我们手动去检测是否是当前类型的?改写代码为如下:

public static void main(String[] args) {
    List list = new ArrayList();
    list.add("1");
    list.add(2);

    for(int i = 0; i< list.size();i++){
        if(list.get(i) instanceof String){
            String item = (String)list.get(i);
        }
        else if(list.get(i) instanceof Integer){
            Integer item = (Integer)list.get(i);
        }
    }
}

此时代码可以正常运行了,但是这混乱的列表中,各种各样的类型都有可能,不好使用,每次都要进行判断是何种类型,才能够正常使用,因此引入了泛型,可以规范指定类型的列表只能传入当前类型

    List<String> list = new ArrayList<>();
    list.add("1");
    list.add(2);// 此时,如果再按以上的输入,就能够看到不符合当前的类型String

泛型的优点

(1)保证了类型的安全性

上述说到的场景其实就是对于安全性的检测,当放入错误的类型的时候,编译器会发现类型不匹配,终止编译,编译失败

    List<String> list = new ArrayList<>();
    list.add(2);// 编译失败

(2)消除强制类型转换

还是上述场景中说到的情况1,就是当不知道一个类型的时候,都属于Object,需要转换为我们想要的类型

Integer a = (Integer) b;

在使用泛型了之后,就能够确定类型,使得代码减少了强制类型转换的步骤,代码可读性也提高了

(3)避免了不必要的拆箱装箱

非泛型编程,消耗资源在装箱拆箱,集合的时候体现的十分明显

Object b = 1;  // 装箱
int a = (int) b;	// 拆箱

泛型编程

public static T getValue<T>(T b){
	return b;
}
public static void main(String[] args){
	int a = getValue<int>(1);  // 指定了泛型类型,也就不会进行拆箱装箱操作了
}

(4)提高代码的重用性

这点可以从集合中看出,比如分别需要只适用于 String 和 只适用 Integer 的列表,当没有泛型的时候,就会有两个实现类

class a{
	String ...
}
class b{
	Integer ...
}

当引入泛型后,就更加可以实现代码的复用,实际上两者的代码都是相似的

class a<E>{
	E ...
}

泛型中占位符 K T V E

在Java已有的类源码中会经常见到类上有泛型

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    ...
}
 
public class HashMap<K,V> extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable {
    ...
}

这些参数其实都是一样的,都仅仅是一个占位符,但为了更高的区别其意义,有了不同的泛型参数名称,常用的如下:
E:Element(在集合中使用,因为集合中存放的是元素)
T:Type(Java类)
K:Key(键)
V:Value(值)
N:Number(数值类型)
?:表示不确定的 java 类型

如何使用泛型

使用方法

使用方式有三种:

  • 泛型类
    可以有多个饭女性类型
    public class 类名 <泛型类型1, 泛型类型2, ...>
    
  • 泛型接口
    public <泛型类型> 返回类型 方法名(泛型类型 变量名);
    
  • 泛型方法
    public <泛型类型> 返回类型 方法名(泛型类型 变量名){
    }
    

public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。
只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。

错误写法:报错,会提示需要引入类T
    public T get(T t){

    }
正确写法:正常,此时的 T 仅仅只是标识位,没有任何意义
    public <T> T get(T t){

    }

使用场景

对于项目开发的过程中,常使用于统一返回接口当中,以统一返回结果举例泛型的使用

/**
 * @description:
 * @author: HWH
 * @create: 2022-10-17 14:56
 **/

public class Result<T>{
    private String code;
    private String msg;
    private T data;
    public Result(String code, String msg, T data){
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static <T> Result success(T data){
        return new Result<>("500", "操作成功", data);
    }
    
	/.. 省略 getSet方法 ../
}

泛型通配符

用于解决泛型之间引用传递问题的特殊语法,有以下三类

  • 无边界的通配符:<?>
    • 接收未知类型的数据
  • 固定上边界的通配符:<? extends E>
    • 固定上边界的通配符的泛型,能够接受指定类及其子类性的数据
  • 固定下边界的通配符:<? super E>
    • 固定下边界,能够接受指定类及其父类类型的数据

泛型的实现原理

JVM实现了泛型擦除机制,在编译的期间,擦除反省语法,并做出类型转换

public class A<T>{
	private T value;
}

在编译器运行后,进行泛型擦除,替换成 Objcet

public class A{
	private Objcet value;
}

一般来说,泛型擦除后由于不知道类型,都替换为所有引用类型的父类Objcet
在使用泛型通配符后,就有可能不是替换成Object,比如

public class B<T extends String>{
	private T value;
}

在擦除后

public class B{
	private String value;
}

由于类型都被擦除成了 Objcet,当我们需要使用里面的内容的时候,还是需要强制类型转换的,但此时不需要我们操作,编译器会自动帮我们添加上相应的转换
引用上面的一段代码

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");

        for(int i = 0; i< list.size();i++){
            String item = list.get(i);
        }
    }

在反编译后

    public static void main(String[] var0) {
        ArrayList var1 = new ArrayList();	// 这里的String已经被类型擦除
        var1.add("1");
        var1.add("2");

        for(int var2 = 0; var2 < var1.size(); ++var2) {
            String var3 = (String)var1.get(var2);	// 这里是编译器帮我们加上的强制类型转换代码
        }
    }

在这里插入图片描述

在字节码中体现为checkcast进行强制类型转换

所以说,编译器其实是没有泛型这个概念的,会将所有的类型进行擦除,然后进行强制类型转换。
泛型的作用就是指定要转换成什么样的类型,以及作为一个安全检测机制的作用,如果不符合类型,不能够通过编译

资料来源

java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
Java 泛型 | 菜鸟教程
Java泛型详解,史上最全图文详解
泛型的基本原理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值