Java面试题-Java核心基础-第九天(泛型)

目录

一、泛型的理解

二、泛型的作用

三、泛型有哪些使用方式

四、上限、下限通配符的使用

五、泛型的原理


一、泛型的理解

泛型在jdk5中开始有的,泛型其实就是将类型进行参数话,使得类型在编译时就确定了,这种类型参数可以用在类、接口、方法上面

泛型的初衷是为了安全和方便的 安全是类型转换安全 方便是指可以不需要类型转换了

它的这种安全体现在如果说编译的时候能够检测出来问题,最好,就不要拖到运行的时候再来出错

有句话正好贴合这里:越早出错,代价越好

例子:

Object s= new String();

Integer s1 = (Interger)s;这个编译的时候不会出错,但是实际运行会出现ClassCastException类型转换异常。

如果说使用了泛型的话,那么是那种类型就得是哪种类型

List<String> list = new ArrayList();

实现安全就体现在添加元素上面,其次如果取元素,那么就只能使用正确的类型进行接收,如果类型不一致接收都接收不了,不能强转成非法的类型

二、泛型的作用

其实作用大的来说就两点:安全 + 方便

安全:避免错误的强转,比如说现在定义一个泛型类型为String类型的集合,那么从中取出来元素就不能强转成其他类型,否则就会报错。如果说它让你强转的话,那么在运行时会出错

参考上面理解中的例子

方便:其实就是不需要手动的强转了,自动的为我们强转...这里的例子,比如说方法返回值接收

当然除了这两点主要的,还有其他的一些好处:

1. 如果说使用了包装类型那么就根本就不需要拆箱与装箱

其实原因就在于指定了上面类型就会自动的返回什么类型 而不会像方法那么传递一个Integer返回一个int造成需要拆箱了

2. 提高了代码的重用性,需要什么类型我就指定好什么类型就行了。比如说一个求和的方法,我不再需要写多个重载的方法了,参数传递什么类型  结果就能是什么类型

三、泛型有哪些使用方式

泛型类

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{private T key;public Generic(T key) {this.key = key;}public T getKey(){return key;}
}

典型应用:用在接口统一结果返回对象 

泛型方法

  public static < E > void printArray( E[] inputArray ){for ( E element : inputArray ){System.out.printf( "%s ", element );}System.out.println();}

泛型方法不等于泛型类中的那个使用泛型属性的方法。那个有局限性:

我如果想要换一种类型,是不是就得重新创建另外一种类型的对象,不方便

典型应用:集合工具类中的排序方法,是需要对各种类型的集合都能排序的

如果是没有泛型方法,那么每次对一个新的类型的集合进行排序,就需要创建一个新的工具类对象

不够灵活

当然还有许多地方:

很多时候我们是需要直接就能得到对应的对象,而不想让得到一个Object类对象,我们再去进行强转。

可以使用泛型方法,参数就写Class<T> clazz 

然后在方法中强转 成T类型

public static <T> T copyBean(Object source,Class<T> clazz){T result = null;try {result = clazz.newInstance();BeanUtils.copyProperties(source,result);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return result;}
 public static <T> List<T> copyBean(List<?> list,Class<T> clazz){return list.stream().map(o->copyBean(o,clazz)).collect(Collectors.toList());}
public <T> T getData(TypeReference<T> typeReference) {Object data = get("data");	//默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}
public <R,ID> R queryShopWithCacheThrough(String cachePrefix, ID id, Class<R> type, Function<ID,R> dbFallBack,Long time, TimeUnit timeUnit){//1. 从redis中查询缓存String key = cachePrefix + id;String json = stringRedisTemplate.opsForValue().get(key);//2. 判断是否存在if(StrUtil.isNotBlank(json)){//3. 若存在,则直接返回R r = JSONUtil.toBean(json, type);return r;}if(json!=null){// 查到空数据,直接返回错误return null;}//4. 若不存在,则查询数据库R r = dbFallBack.apply(id);//5. 不存在,返回错误if (r == null) {//缓存空数据stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//6. 存在,存入redisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(r),time,timeUnit);//7. 返回return r;}

泛型接口:

public interface Generator<T> {public T method();
}

实现泛型接口,不指定类型:

class GeneratorImpl<T> implements Generator<T>{@Overridepublic T method() {return null;}
}

典型应用:

在MP中的mapper接口、service接口都使用到了泛型接口 :

 MP中的mapper接口和service接口使用的就是上面那种  通过指定实体类的类型,从而确定是对哪张表进行操作,所以此时不能再传递参数的时候传递其他表对应的实体类了,以及wrapper也是

public interface CourseBaseMapper extends BaseMapper<CourseBase> {}
public interface BaseMapper<T> extends Mapper<T> {int insert(T entity);
}

 service:

public interface CourseBaseService extends IService<CourseBase> {}
public interface IService<T> {int DEFAULT_BATCH_SIZE = 1000;default boolean save(T entity) {return SqlHelper.retBool(this.getBaseMapper().insert(entity));}
}

四、上限、下限通配符的使用

首先介绍? 这个代表所有 是所有 T 的根父类

比如说我可以这样   List<?> list =  new ArrayList();

List<String> list1 = new ArrayList();

将list1赋值给list    list = list1 这样不会报错

但是如果我是    List<Object> list = new ArrayList();

List<String> list1 = new ArrayList(); 那么就会报错。

小插一句:这里如果是 Person<T> p = new Person(); Student<T> s = new Student();

那么可以将 s 赋值给 p  p = s 这里就是单纯的子父类了

原因在于相当于 如果这样可以的话  相当于集合中现在就只能添加String类型的元素了,相当于就是可添加的范围减少了,显然这是不合理的,另外还有就是你声明的类型是 Object而实际的类型是String  那我如果取元素,那么取出来的元素到底应该是String还是Object呢?其实就说不清了,存也是一样,我到底是存String还是存Object呢?

其实通配符的作用就来了,使用?就可以代替Object 就可以接收任意泛型类型

如果只是使用? 那么就是对泛型类型没有约束,什么类型都可以。但是如果你想要指定的范围,比如说我要求你给我的泛型类型必须是某一个类的父类或者是子类 ,那么就可以使用带范围的通配符。

<? extends Person>这个代表泛型类型只能是Person类型或者是其子类 也就是规定了上限

<? super Person>这个代表泛型类型只能是Person类型或者是Person的父类,规定了下限

那么方法参数的泛型类型就必须老实按规定来。

注意:

当集合类型的泛型定义为通配符的时候,是不能往里面添加元素的。只能存null 只能取元素

比如说:

List<?> list = new ArrayList(); list.add("avc") 这是会编译报错的。但是可以存null

这里看似应该是可以传递的,因为是?可以代表是Object的意思,但是为什么不能传,只能存null可能就是它这么规定的吧。

这个获取到集合中的元素,元素类型是Object类型

另外还有有范围的,比如说:

List<? extends Person> list = new ArrayList<>()

也只能存null,这个好理解,因为怕我存Person的父类对象进去了,为了安全起见,干脆就只能让你存null进去,这个因为无论怎样元素一定是Person类型或者是其子类类型,那么所以就完全使用Person类就可以接收

反过来如果是super,它怕你存进去Person的子类所以直接不让你存,就只能让你存null

这里因为不知道存进去的到底有多大,因此这个取出来直接使用根父类Object类接收

使用例子:

public static <T> List<T> copyBean(List<?> list,Class<T> clazz){return list.stream().map(o->copyBean(o,clazz)).collect(Collectors.toList());}

因为这里我不确定到底是需要对什么类型的集合进行拷贝,但是这里又不能写死成Object,所以就只能使用?通配符了,当然也可以使用 T的方式,那么相当于就多了一个泛型参数,所以在前面定义的时候也需要多给一个,就是下面这样:

public static <T,O> List<T> copyBean(List<O> list,Class<T> clazz){return list.stream().map(o->copyBean(o,clazz)).collect(Collectors.toList());}

五、泛型的原理

原理就是泛型擦除,其实泛型信息只会在编译时候保持,运行时就全没了,字节码中其实有泛型和没泛型的是一样的   如果没有设定范围  那么会被擦除成Object类型  如果给定了范围 那么擦除之后就是对应类型的 所以在编译的时候可以使用到编译器进行检查,因此这里不涉及到JVM,因此泛型是几乎不消耗性能的,所以使用泛型的另外一个作用就是:潜在的性能收益  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/114223.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决Docker安装MySQL不区分大小写问题

Docker安装MySQL忽略大小写问题的问题 连接MySQL&#xff1a; 查看当前mysql的大小写敏感配置 show global variables like %lower_case%; ------------------------------- | Variable_name | Value | ------------------------------- | lower_case_file_system …

openEuler 服务器安装 JumpServer (all-in-one 模式)

openEuler 服务器安装 JumpServer JumpServer 简介什么是 JumpServer &#xff1f;JumpServer 的各种类型资产JumpServer 产品特色或优势JumpServer 符合 4A 规范 JumpServer 系统架构应用架构组件说明 JumpServer 安装部署环境要求网络端口网络端口列表防火墙常用命令 在线脚本…

LeetCode 414. Third Maximum Number【数组】简单

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

vlookup函数踩坑(wps)

使用wps的朋友看过来 vlookup函数踩坑&#xff0c;vlookup&#xff08;查找值&#xff0c;查找范围&#xff0c;返回值的索引&#xff0c;精确查找or模糊查找&#xff09; 我们要查找的数据的那一列&#xff0c;必须是查找范围的第一列&#xff01; 案例&#xff0c;看下面的…

02、Python 字符串

目录 字符串的基础用法字符串包含引号字符串拼接获取用户输入长字符串原始字符串字节串字符串与字节串转换 字符串的基础用法 列字符串的内容几乎可以包含任何字符&#xff0c;英文字符也行&#xff0c;中文字符也行。 既可用单引号&#xff0c;也可用双引号 字符串包含引号…

shell的for循环与结构化

shell笔记 列表for循环不带列表for循环for循环举例1.例1 所有文件名大写替换为小写2. 例2 读取/etc/passwd文件&#xff0c;依次输出ip段3. 例3 读取/etc/hosts内容for循环&#xff0c;执行ping4. 例4 循环ip列表&#xff0c;输出对应编号5. 例5 批量添加用户 break1. 例1 brea…

C++ vector 的模拟实现

目录 1. vector 类的成员变量 2. 无参构造 3. 析构函数 4. size_t capacity() 5. size_t size() 6. void reserve(size_t n) 7. 迭代器 8. void push_back(const T& x) 9. T& operator[](size_t pos) 10. iterator insert(iterator pos, const T& val…

使用Redis发布订阅模式实现 Session共享

其实并不是实现session共享&#xff0c;而是通过redis的发布订阅&#xff0c;让所有集群的服务器&#xff0c;都让自己的session发送一下消息。比如说userId在第35台服务器上&#xff0c; 有100台服务器&#xff0c;那么第1台服务器收到消息&#xff0c;需要通知userId&#xf…

vue 组件封装 综合案例2

vue 组件封装 综合案例2 main.js import Vue from vue import App from ./App.vueVue.config.productionTip false//封装全局指令 focus Vue.directive(focus, {// 指令所在的dom元素&#xff0c;被插入到页面中时触发inserted(el) {el.focus();} })new Vue({render: h >…

MySQL 三大日志(bin log、redo log、undo log)

redo log redo log (重做日志) 是 InnoDB 存储引擎独有的&#xff0c;它让 MySQL有了崩溃恢复的能力&#xff0c;是事务中实现 持久化的重要操作 比如 MySQL 实例宕机了&#xff0c;重启时&#xff0c;InnoDB 存储引擎会使用 redo log 恢复数据&#xff0c;保证数据的持久性与…

设计模式——七大原则详解

目录 设计模式单一职责原则应用实例注意事项和细节 接口隔离原则应用实例 依赖倒转&#xff08;倒置&#xff09;原则基本介绍实例代码依赖关系传递的三种方式注意事项和细节 里氏替换原则基本介绍实例代码 开闭原则基本介绍实例代码 迪米特法则基本介绍实例代码注意事项和细节…

golang笔记17--编译调试go源码

golang笔记17--编译调试go源码 前置条件编译源码在 fmt 包中加自定义函数说明 当前go语言越来越流行了&#xff0c;各大厂商都有加大go工程师的需求&#xff0c;作为go语言的学习者&#xff0c;我们除了要了解如何使用go语言外&#xff0c;也有必要了解一下如何编译、调试go源码…

解决XXLJOB重复执行问题--Redis加锁+注解+AOP

基于Redis加锁注解AOP解决JOB重复执行问题 现象解决方案自定义注解定义AOP策略redis 加锁实践 现象 线上xxljob有时候会遇到同一个任务在调度的时候重复执行&#xff0c;如下图&#xff1a; 线上JOB服务运行了2个实例&#xff0c;有时候会重复调度到同一个实例&#xff0c;有…

交换机端口灯常亮 端口up状态 服务器设置ip交换机获取不到服务器网卡mac地址 不能通信

环境: 深信服防火墙 8.0.75 AF-2000-FH2130B-SC S6520X-24ST-SI交换机 version 7.1.070, Release 6530P02 问题描述: 交换机一个vlan下有3台服务器,连接端口2、3、4,2和3连接的服务器正常,交换机3端口灯常亮 端口up状态 服务器自动获取不了地址,改为手动设置ip后,交…

力扣labuladong——一刷day01

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、21. 合并两个有序链表二、力扣86. 分隔链表三、力扣23. 合并 K 个升序链表四、力扣删除链表的倒数第 N 个结点 前言 一、21. 合并两个有序链表 /*** Defin…

微信小程序 onLoad和onShow的区别

在微信小程序中&#xff0c;onLoad() 和 onShow() 是两个常用的生命周期函数&#xff0c;用于监听页面的加载和显示事件。这两个函数的区别如下&#xff1a; 触发时机 onLoad() 函数只会在页面加载时触发一次&#xff0c;而 onShow() 函数每次页面显示时都会被触发。因此&#…

Xubuntu16.04系统中安装create_ap创建无线AP

1.背景说明 在Xubuntu16.04系统的设备上安装无线WIFI模块后&#xff0c;想通过设备自身的无线AP&#xff0c;进行和外部设备的连接&#xff0c;需要安装create_ap软件&#xff0c;并设置无线AP的名称和密码&#xff0c;并设置为开机自启动。 create_ap是一个用于在Linux系统上创…

Addressable使用指南

1、基础用法就不再赘述了&#xff0c;重要的属性配置&#xff1a; Disable Catalog Update on Startup&#xff1a;禁用时在初始化Addressables的时候自动更新远程的catalog&#xff08;启用后可以通过代码 Addressables.CheckForCatalogUpdates()更新&#xff09; Use…

开源贡献难吗?

本文整理自字节跳动 Flink SQL 技术负责人李本超在 CommunityOverCode Asia 2023 上的 Keynote 演讲&#xff0c;李本超根据自己在开源社区的贡献经历&#xff0c;基于他在贡献开源社区过程中的一些小故事和思考&#xff0c;如何克服困难&#xff0c;在开源社区取得突破&#x…

BetaFlight飞控AOCODAF435V2MPU6500固件编译

BetaFlight飞控AOCODAF435V2MPU6500固件编译 1. 源由2. 准备2.1 板子2.2 代码2.3 工具 3. 配置修改4. 编译4.1 获取代码4.2 获取配置4.3 编译固件4.4 DFU烧录4.5 版本核对 5. 总结 1. 源由 刚拿到一块Aocoda F405V2 (MPU6500) AT32F435飞控板(替换主控芯片)。 Aocoda-RC F40…