Java泛型背后是什么,揭秘!Java 泛型背后到底是什么?

本文深入探讨了Java中的泛型应用,解释了泛型类型擦除的概念,即编译后的泛型在JVM中都以Object类型存在。通过实例展示了泛型如何限制代码规范,减少类型转换,并介绍了泛型在类、接口和方法上的应用。同时,讨论了通配符的使用场景,如上限和下限泛型约束,以及它们在不同情况下的限制。泛型在实际开发中的重要作用,特别是在面试和MVP模式中的应用,也被强调。
摘要由CSDN通过智能技术生成

这一节主要讲的内容是java中泛型的应用,通过该篇让大家更好地理解泛型,以及面试中经常说的泛型类型擦除是什么概念,今天就带着这几个问题一起看下:

举一个简单的例子:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-1.jpg (37.5 KB, 下载次数: 0)

2021-2-16 00:06 上传

这里可以看出来在代码编写阶段就已经报错了,不能往string类型的集合中添加int类型的数据。

那可不可以往List集合中添加多个类型的数据呢,答案是可以的,其实我们可以把list集合当成普通的类也是没问题的,那么就有下面的代码:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-2.jpg (37.92 KB, 下载次数: 0)

2021-2-16 00:06 上传

从这里可以看出来,不定义泛型也是可以往集合中添加数据的,所以说泛型只是一种类型的规范,在代码编写阶段起一种限制。

下面我们通过例子来介绍泛型背后数据是什么类型

public class BaseBean {    T value;    public T getValue() {        return value;    }    public void setValue(T value) {        this.value = value;    }}

上面定义了一个泛型的类,然后我们通过反射获取属性和getValue方法返回的数据类型:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-3.jpg (51.31 KB, 下载次数: 0)

2021-2-16 00:06 上传

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-4.jpg (26.55 KB, 下载次数: 0)

2021-2-16 00:06 上传

从日志上看到通过反射获取到的属性是Object类型的,在方法中返回的是string类型,因此咋们可以思考在getValue方法里面实际是做了个强转的动作,将object类型的value强转成string类型。

是的,没错,因为泛型只是为了约束我们规范代码,而对于编译完之后的class交给虚拟机后,对于虚拟机它是没有泛型的说法的,所有的泛型在它看来都是object类型,因此泛型擦除是对于虚拟机而言的。

下面我们再来看一种泛型结构:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-5.jpg (20.63 KB, 下载次数: 0)

2021-2-16 00:06 上传

这里我将泛型加了个关键字extends,对于泛型写得多的伙伴们来说,extends是约束了泛型是向下继承的,最后我们通过反射获取value的类型是String类型的,因此这里也不难看出,加extends关键字其实最终目的是约束泛型是属于哪一类的。所以我们在编写代码的时候如果没有向下兼容类型,会警告错误的:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-6.jpg (28.44 KB, 下载次数: 0)

2021-2-16 00:06 上传

大家有没有想过为啥要用泛型呢,既然说了泛型其实对于jvm来说都是Object类型的,那咱们直接将类型定义成Object不就是的了,这种做法是可以,但是在拿到Object类型值之后,自己还得强转,因此泛型减少了代码的强转工作,而将这些工作交给了虚拟机。

比如下面我们没有定义泛型的例子:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-7.jpg (27.72 KB, 下载次数: 0)

2021-2-16 00:06 上传

势必在getValue的时候代码有个强转的过程,因此在能用泛型的时候,尽量用泛型来写,而且我认为一个好的架构师,业务的抽取是离不开泛型的定义。

常见的泛型主要有作用在普通类上面,作用在抽象类、接口、静态或非静态方法上。

类上面的泛型

比如实际项目中,我们经常会遇到服务端返回的接口中都有errMsg、status等公共返回信息,而变动的数据结构是data信息,因此我们可以抽取公共的BaseBean:public class BaseBean {    public String errMsg;    public T data;    public int status;}

抽象类或接口上的泛型

//抽象类泛型public abstract class BaseAdapter {    List DATAS;}//接口泛型public interface Factory {    T create();}//方法泛型public static T getData() {    return null;}

多元泛型

public interface Base {    void setKey(K k);    V getValue();}

泛型二级抽象类或接口

public interface BaseCommon extends Base {}//或抽象类public abstract class BaseCommon implements Base {}

抽象里面包含抽象

public interface Base {   //    void setKey(K k);    V getValue();   void addNode(Map map);   Map getNode(int index);}public abstract class BaseCommon implements Base {   //多重泛型   LinkedList> DATAS = new LinkedList<>();   @Override   public void addNode(Map map) {       DATAS.addLast(map);   }   @Override   public Map getNode(int index) {       return DATAS.get(index);   }}

通配符 >通配符和区别是在你不知道泛型类型的时候,可以用通配符来定义,下面通过一个例子来看看的用处:

//定义了一个普通类public class BaseBean {    T value;    public T getValue() {        return value;    }    public void setValue(T value) {        this.value = value;    }}//用来定义泛型的public class Common1 extends Common {}

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-8.jpg (55.7 KB, 下载次数: 0)

2021-2-16 00:06 上传

在定义的时候将Common的泛型指向Common1的泛型,可以看到直接提示有问题,这里可以想,虽然Common1是继承自Common的,但是并不代表BaseBean之间是等量的,在开篇也讲过,如果泛型传入的是什么类型,那么在BaseBean中的getValue返回的类型就是什么,因此可以想两个不同的泛型类肯定是不等价的,但是如果我这里写呢:

public static void main(String\[\] args) {    BaseBean commonBaseBean = new BaseBean<>();    //通配符定义就没有问题    BaseBean> common1BaseBean = commonBaseBean;    try {        //通过反射猜测setValue的参数是Object类型的        Method setValue = common1BaseBean.getClass().getDeclaredMethod("setValue", Object.class);        setValue.invoke(common1BaseBean, "123");        Object value = common1BaseBean.getValue();        System.out.println("result:" + value);    } catch (NoSuchMethodException e) {        e.printStackTrace();    } catch (IllegalAccessException e) {        e.printStackTrace();    } catch (InvocationTargetException e) {        e.printStackTrace();    }}

在上面如果定义的泛型是通配符是可以等价的,因为此时的setValue的参数是Object类型,所以能直接将上面定义的泛型赋给通配符的BaseBean。

通配符不能定义在类上面、接口或方法上,只能作用在方法的参数上

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-9.jpg (15.99 KB, 下载次数: 0)

2021-2-16 00:06 上传

其他的几种情况自己去尝试,正确的使用通配符:

public void setClass(Class> class){    //todo}

、、 extends >、 super >表示上限泛型、表示下限泛型为了演示这两个通配符的作用,增加了一个类:

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-10.jpg (15.89 KB, 下载次数: 0)

2021-2-16 00:06 上传

//新增加的一个BaseCommonpublic class Common extends BaseCommon{}

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-11.jpg (28.74 KB, 下载次数: 0)

2021-2-16 00:06 上传

第二个定义的泛型是不合法的,因为BaseCommon是Common的父类,超出了Common的类型范围。

不能作用在类、接口、方法上,只能通过方法传参来定义泛型 在BaseBean里面定义了个方法:

public void add(Class super Common> clazz) {}

a3318c941136e31422f9e0be7c9e8802.gif

揭秘!Java 泛型背后到底是什么?-12.jpg (30.18 KB, 下载次数: 0)

2021-2-16 00:06 上传

可以看到当传进去的是Common1.class的时候是不合法的,因为在add方法中需要传入Common父类的字节码对象,而Common1是继承自Common,所以直接不合法。

在实际开发中其实知道什么时候定义什么类型的泛型就ok,在mvp实际案例中泛型用得比较广泛,大家可以根据实际项目来找找泛型的感觉,只是面试的时候需要理解类型擦除是针对谁而言的。

类型擦除 其实在开篇的时候已经通过例子说明了,通过反射绕开泛型的定义,也说明了类中定义的泛型最终是以Object被jvm执行。所有的泛型在jvm中执行的时候,都是以Object对象存在的,加泛型只是为了一种代码的规范,避免了开发过程中再次强转。

泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值