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

举一个简单的例子:

image.png

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

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

image.png

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

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

public class BaseBean {

T value;

public T getValue() {

return value;

}

public void setValue(T value) {

this.value = value;

}

}

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

image.png

image.png

从日志上看到通过反射获取到的属性是Object类型的,在方法中返回的是string类型,因此咋们可以思考在getValue方法里面实际是做了个强转的动作,将object类型的value强转成string类型。是的,没错,因为泛型只是为了约束我们规范代码,而对于编译完之后的class交给虚拟机后,对于虚拟机它是没有泛型的说法的,所有的泛型在它看来都是object类型,因此泛型擦除是对于虚拟机而言的。

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

image.png

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

image.png

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

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

image.png

势必在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 {

}

image.png

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

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

image.png

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

public void setClass(Class> class){

//todo

}

、、 extends >、 super >

表示上限泛型、表示下限泛型

为了演示这两个通配符的作用,增加了一个类:

image.png

//新增加的一个BaseCommon

public class Common extends BaseCommon{

}

image.png

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

不能作用在类、接口、方法上,只能通过方法传参来定义泛型

在BaseBean里面定义了个方法:

public void add(Class super Common> clazz) {

}

image.png

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

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

类型擦除

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

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

什么是java泛型_java泛型背后是什么相关推荐

  1. java泛型的泛型_Java 泛型总结(一):基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java ...

  2. java模型给泛型_java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应 ...

  3. java 泛型嵌套泛型_Java泛型简介–第6部分

    java 泛型嵌套泛型 这是关于泛型的介绍性讨论的延续, 此处的先前部分可以在此处找到. 在上一篇文章中,我们讨论了关于类型参数的递归边界. 我们看到了递归绑定如何帮助我们重用了车辆比较逻辑. 在该文 ...

  4. java 获取泛型_Java泛型 | Jackson TypeReference获取泛型类型信息

    前言 Jackson是一个比较流行的Json序列化和反序列化框架.本文以Jackson为例介绍TypeReference实现涉及泛型的反序列化,及TypeReference的实现原理.对于获取泛型类型 ...

  5. java泛型类指定多个泛型_Java泛型中的多态

    java泛型类指定多个泛型 从作为Java程序员的早期开始,我们都知道如何实例化和使用Collection对象. 实例化为具体类的List接口将如下所示. List myArrayList = new ...

  6. java怎么使用泛型_java泛型 7 泛型的基本介绍和使用

    现在开始深入学习Java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 一.泛型的基本概念 泛型的定义:泛型是 ...

  7. java 高级泛型_java泛型的高级应用

    展开全部 在上面的例子中,由于没有限制class GenericsFoo类型持有者T的范围,实际上这里32313133353236313431303231363533e59b9ee7ad9431333 ...

  8. 泛型 java 总结_JAVA泛型总结

    泛型是JDK1.5的新东西,总结了一下,以后备忘, 泛型也叫generics,泛型分泛型方法和泛型类,两种定义方式,泛型还有上界下界的说法, 先看看什么叫泛型方法和泛型类,一下是网上的一个解释,个人觉 ...

  9. r java 泛型_Java 泛型

    规则和限制 1.泛型的类型参数只能是类类型(包括自定义类),不能是简单类型. 2.同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的. 3.泛型的类型参数可以有多个. ...

最新文章

  1. 中国电子学会图形化四级编程题:绘制雪花
  2. 苹果微信更新不了最新版本_微信版本又双叒叕更新了,这个功能正式下线
  3. 把struts2的struts.xml配置文件分解成多个配置文件
  4. SpringBoot启动过程详解
  5. 启动DevStack的各项服务
  6. (八)boost库之异常处理
  7. 如何绕过 TPM 2.0 安装 Windows 11 操作系统?
  8. python 千位分隔符_玩转千位分隔符输出 - leejun2005的个人页面 - OSCHINA - 中文开源技术交流社区...
  9. 软件测试的基础知识(五)
  10. Telnet命令检测远程主机上的端口是否开启
  11. 设计模式之构造函数模式
  12. ELMo ,LM:一串词序列的概率分布probability distribution over sequences of words
  13. Microsoft Visio 软件的使用
  14. (转)高德百度坐标系转换方法
  15. bigdecimal负数变正数_Java中BigDecimal的8种舍入模式
  16. Ubuntu14.04 Firefox无法播放视频
  17. 设计模式之设配器模式
  18. ubantu22与windows相互复制粘贴(详细图文)
  19. coding ssh端口指定_CODING添加部署账户SSH公钥
  20. python无序序列_Python自动化Markdown无序列表

热门文章

  1. 23种设计模式C++源码与UML实现--中介者模式
  2. UDP协议抓包分析 -- wireshark
  3. os模块中的shutil的使用方式与方法
  4. SI 和 DI 寄存器的区别
  5. 反病毒引擎设计全解(一)
  6. Spring.ImportSelector接口
  7. 操作系统(三十)避免死锁
  8. Judges' Time Calculation
  9. c# gerber文件读取_懒猪编程实例六:Visual C# 实现外部文件的读取和写入
  10. Truffle合约交互 - WEB端对以太坊数据的读写