作者:的一幕
链接:www.jianshu.com/p/dd34211f2565

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

举一个简单的例子:


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

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


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

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

public class BaseBean {    T value;

    public T getValue() {        return value;    }

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

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



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

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

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


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


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

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


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

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

类上面的泛型

比如实际项目中,我们经常会遇到服务端返回的接口中都有errMsgstatus等公共返回信息,而变动的数据结构是data信息,因此我们可以抽取公共的BaseBean

public class BaseBean {    public String errMsg;    public T data;    public int status;}

抽象类或接口上的泛型

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

多元泛型

public interface Base<K, V> {    void setKey(K k);

    V getValue();}

泛型二级抽象类或接口

public interface BaseCommon<K extends Common1, V> extends Base<K, V> {}//或抽象类public abstract class BaseCommon implements Base {}

抽象里面包含抽象

public interface Base<K, V> {   //    void setKey(K k);    V getValue();   void addNode(Map map);

   Map getNode(int index);}public abstract class BaseCommon<K, V> implements Base<K, V> {   //多重泛型   LinkedList> DATAS = new LinkedList<>();@Overridepublic void addNode(Map map) {       DATAS.addLast(map);   }@Overridepublic 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 {}

在定义的时候将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。

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


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

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

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


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

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

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

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

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

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

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

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

(完)

最近热文:分享一份Java架构师学习资料!数据库面试没那么难,不信你看这24道题!错误日志这样排查,干活更得劲了!!2020年 3月全国程序员工资新鲜出炉!Java 8 的 Stream API 这牛X,性能如何?面试必问41道 SpringBoot 面试题!减少该死的 if else 嵌套,拯救强迫症!1.3万亿条数据查询如何做到毫秒级响应?

——长按关注Java大后端——

戳原文,获取一份面试题资料!

什么是抽象类?抽象类的作用_揭秘!Java 泛型背后到底是什么?相关推荐

  1. 泛型类有什么作用_3 分钟带你彻底搞懂 Java 泛型背后的秘密

    优质文章,及时送达 作者 | 的一幕 来源 | www.jianshu.com/p/dd34211f2565 这一节主要讲的内容是java中泛型的应用,通过该篇让大家更好地理解泛型,以及面试中经常说的 ...

  2. java多态 作用_关于Java三大特性之多态的总结

    面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 1.定义: 多态:指允许不同类的对象对同一消息做出响应.即同一消息可 ...

  3. java 克隆的作用_关于java中克隆的学习(一)

    java中的克隆,就是要复制对象,但为什么要用克隆呢?我们直接把对象赋值给其它同类型的实例不就行了吗?这就要从java的值传递和引用传递说起了. package dcr.study.test.poin ...

  4. java枚举对象作用_浅析Java编程中枚举类型的定义与使用

    定义枚举类型时本质上就是在定义一个类,只不过很多细节由编译器帮您补齐了,所以某些程度上,enum关键字的 作用就像是class或interface. 当您使用"enum"定义枚举类 ...

  5. python中间件的作用_引用java作用

    浅谈AndroidManifest.xml与R.java及各个目录的作用 原文:浅谈AndroidManifest.xml与R.java及各个目录的作用 在开发Android项目中,AndroidMa ...

  6. Java main方法_解释Java中的main方法,及其作用_一个java文件中可包含多个main方法

    public static void main(String[] args) {} 或者 public static void main(String args[]) {} main方法是我们学习Ja ...

  7. java 线程组作用_浅析Java中线程组(ThreadGroup类)

    一.概念 Java中使用ThreadGroup类来代表线程组,表示一组线程的集合,可以对一批线程和线程组进行管理.可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线 ...

  8. java 模仿支付成功_揭秘Java工程师薪资为何这么高,教你如何成为架构师拿50W年薪!...

    Java工程师数量日益增长,而其薪资不降反升几十年来,Java比其他语言更常名列榜首2019年,Java仍然是最流行的编程语言 Java工程师的薪资到底多高? 据职友集数据,近一年,全国Java工程师 ...

  9. 中service层的作用_浅析Java中dto、dao、service、controller的四层结构

    目前我所在的项目组采用的是SpringBoot框架,前端使用BootStrap+jQuery. SpringBoot是BS开发框架之一,不用单独开启tomcat服务器,目前比较流行,一般开发大型项目时 ...

最新文章

  1. oracle 条件动态视图,oracle最重要的9个动态性能视图
  2. 修改grub2安装双系统的windows引导
  3. 深度剖析Kubernetes API Server三部曲 - part 2
  4. 大数运算(7)——大数阶乘(求阶乘)
  5. 新手引导动画的4种实现方式
  6. boost::signals2模块实现定制组合器的测试程序
  7. redis module模块简单使用
  8. Android之动画
  9. DOM BOM document window 区别
  10. java 陷阱,java 中的陷阱。
  11. 团队开发里频繁使用 git rebase 来保持树的整洁好吗?
  12. 一个声称南方985普通学生,前端学习10个月,秋招斩获字节跳动offer的分享!
  13. Java自学如何找工作?
  14. 洛谷1498-谢尔宾斯基三角形-python-(递归)
  15. 打造更安全的视频加密,云点播版权保护实践
  16. java虚拟机启动失败_Eclipse 启动提示java虚拟机启动失败
  17. 源码编译更新nginx到最新版本,并开始nginx支持http2协议模块.
  18. 恐怕你确定自己喜欢做什么
  19. 【数据结构算法】-- C语言
  20. Zeal 面向开发者的离线文档查看工具

热门文章

  1. 文件句柄(file handles) 文件描述符(file descriptors)
  2. 一个 bad file descriptor 的问题
  3. Python中的高阶变量
  4. 苹果降低应用商店收入一半分成、Twitter视频分享功能 Fleet、百度36亿美元收购 YY|Decode the Week...
  5. 网易易盾李雨珂:服务性能+算法确定性优化,迎接5G时代内容安全爆发式流量增长...
  6. 基于Licode的WebRTC全球分布式架构
  7. RTMP之后,SRT与QUIC
  8. 全平台硬件解码渲染方法与优化实践
  9. 写 Python 到底用什么编辑器好?鹅厂程序猿吵翻了
  10. 腾讯数平精准推荐 | 横扫ICDAR 2019,斩获七项冠军