重学Java (一) 泛型
1. 前言
泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接
- Java 泛型详解
- The Java™ Tutorials (Lesson: Generics)
这篇文章就简答聊一下,我实际在开发工作中很少用的到泛型方法这个知识点,以及在实际项目中有哪些东西会使用到泛型。
2. 泛型方法
在阅读代码的时候我们经常会看到下面这样的方法 (这段代码摘自 java.util.AbstractCollection
)
public <T> T[] toArray(T[] a) {// Estimate size of array; be prepared to see more or fewer elementsint size = size();T[] r = a.length >= size ? a :(T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);Iterator<E> it = iterator();for (int i = 0; i < r.length; i++) {if (! it.hasNext()) { // fewer elements than expectedif (a == r) {r[i] = null; // null-terminate} else if (a.length < i) {return Arrays.copyOf(r, i);} else {System.arraycopy(r, 0, a, 0, i);if (a.length > i) {a[i] = null;}}return a;}r[i] = (T)it.next();}// more elements than expectedreturn it.hasNext() ? finishToArray(r, it) : r;
}
那么 pulic
关键字后面的那个 <T>
就是用来标记这个方法是一个泛型方法。 那什么是泛型方法呢。
官方的解释是这样的
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
通俗点来将就是将一个方法泛型化,让一个普通的类的某一个方法具有泛型功能。 如果在一个泛型类中增加一个泛型方法,那这个泛型方法就可以有一套独立于这个类的泛型类型。
通过一个简单的例子, 我们来看看
/*** GenericClass 这个泛型类是一个简单的套皮的 HashMap*/
public class GenericClass<K, V> {private Map<K, V> map = new HashMap<>();public V put(K key, V value) {return map.put(key, value);}public V get(K key) {return map.get(key);}// 泛型方法 genericMethod 可以接受一个全新的、作用域只限本函数的泛型类型Tpublic <T> T genericMethod(T t) {return t;}}
实际使用起来
GenericClass<String, Integer> map = new GenericClass<>();
// put 和 get 方法的参数必须使用定义时指定的 String 和 Integer
System.out.println(map.put("One", 1));
System.out.println(map.get("One"));
// 泛型方法 genericMethod 就可以接受一个 String 和 Integer 以外的类型
System.out.println(map.genericMethod(new Double(1.0)).getClass());
我们再来看看 JDK 中使用到泛型方法的例子。我们最常使用的泛型容器 ArrayList
中有个 toArray
方法。JDK 在它的实现中就提供了两个版本,其中一个就是泛型方法的版本
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{// 这是一个普通版本,返回一个Object的数组public Object[] toArray() {return Arrays.copyOf(elementData, size);}// 这是一个泛型方法的版本,将容器里存储的元素输出到 T[] 数组中。 其中 T 必须是 E 的父类,否则 System.arraycopy 会抛出 ArrayStoreException 异常 public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}
}
泛型方法总体上来说就是可以给与现有的方法实现上,增加一个更加灵活的实现可能。
3. 实战应用
在实际的项目中,对于泛型的使用,除了像倾倒垃圾一样往泛型容易里塞各种 java bean 和其他泛型对象。还能怎么使用泛型呢?
我们在实际的一些项目中,会对数据库中的一些表(多数时候是全部)先实现 CRUD (Create, Read, Update, Delete)的操作,并从这些操作中延伸出一些简单的 REST 风格的 WebAPI 接口,然后才会根据实际业务需要实现一些更复杂的业务接口。
大体上会是下面这个样子。
// 这是一个简单的 Entity 对象
// 通常现在的 Java 应用都会使用到 Lombok 和 Spring Boot
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user")
public class User {@Idprivate Long id;private String username;private String password;
}// 然后这个是 DAO 接口继承自 spring-data 的 JpaRepository
public interface UserDao extends JpaRepository<User, Long> {
}// 在来是一个访问 User 资源的 Service 和他的实现
public interface UserService {List<User> findAll();Optional<User> findById(Long id);User save (User user)void deleteById(Long id);
}@Service
public class UserSerivceImpl implements UserService {private UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic List<User> findAll() {return this.dao.findAll();}@Overridepublic Optional<User> findById(Long id) {return this.dao.findById(id);}@Overridepublic User save(User user) {return this.dao.save(user);}@Overridepublic void deleteById(Long id) {this.dao.deleteById(id);}
}// 最后就是 WebAPI 的接口了
@RestController
@RequestMapping("/user/")
public class UserController{private UserService userService;public UserController(userService userService) {this.userService = userService;}@GetMapping@ResponseBodypublic List<User> fetch() {return this.userService.findAll();}@GetMapping("{id}")@ResponseBodypublic User get(@PathVariable("id") Long id) {// 由于是示例这里就不考虑没有数据的情况了return this.userService.findById(id).get();}@PostMapping@ResponseBodypublic User create(@RequestBody User user) {return this.userService.save(user);}@PutMapping("{id}")@ResponseBodypublic User update(@RequestBody User user) {return this.userService.save(user);}@DeleteMapping("{id}")@ResponseBodypublic User delete(@PathVariable("id") Long id) {User user = this.userService.findById(id);this.userService.deleteById(id);return user;}
}
大致一个表的一套相关接口就是这个样子的。如果你的数据库中有大量表的话,而且每个表都需要提供 REST 风格的 WebAPI 接口的话,那么这将是一个相当枯燥的而又及其容易出错的工作。
为了不让这项枯燥而又容易犯错的工作占去我们宝贵的私人时间,我们可以通过泛型和继承的技巧来重构从 Service 层到 Controller 的这段代码(感谢 spring-data 提供了 JpaRepository
, 让我们不至于从 DAO 层重构)
3.1 Service 层的重构
首先是 Service 接口的重构,我们 Service 层接口就是定义了一组 CRUD 的操作,我们可以将这组 CRUD 操作抽象到一个父接口,然后所有 Service 层的接口都将继承自这个父接口。而接口中出现的 Entity 和主键的类型(上例中 User 的主键 id 的类型是 Long)就可以用泛型来展现。
// 这里泛型表示 E 来指代 Entity, ID 用来指代 Entity 主键的类型
public interface ICrudService<E, ID> {List<E> findAll();Optional<E> findById(ID id);E save(E e);void deleteById(ID id);
}// 然后 Service 层的接口,就可以简化成这样
public interface UserService extends ICrudService<User, Long> {
}
同样 Service 层的实现也可以使用相似的方法具体实现可以抽象到一个基类中。
// 相比 ICrudService 这里有多了一个泛型 T 来代表 Entity 对应的 DAO, 我们的每一个 DAO 都继承自
// spring-data 的 JpaRepository 所以,这里可以使用到泛型的边界
public abstract class AbstractCrudService<T extends JpaRepository<E, ID>, E, ID> {private T dao;public AbstractCrudService(T dao) {this.dao = dao;}public List<E> findAll() {return this.dao.findAll();}public Optional<E> findById(ID id) {return this.dao.findById(id);}public E save(E e) {return this.dao.save(e);}public void deleteById(ID id) {this.dao.deleteById(id);}
}// 那 Service 的实现类可以简化成这样
@Service
public class UserServiceImpl extends AbstractCrudService<UserDao, User, Long> implements UserService {public UserServiceImpl(UserDao dao) {supper(dao);}
}
同样我们可以通过相同的方法来对 Controller 层进行重构
// Controller 层的基类
public abstract class AbstractCrudController<T extends ICrudService<E, ID>, E, ID> {private T service;public AbstractCrudController(T service) {this.service = service;}@GetMapping@ResponseBodypublic List<E> fetch() {return this.service.findAll();}@GetMapping("{id}")@ResponseBodypublic E get(@PathVariable("id") ID id) {// 由于是示例这里就不考虑没有数据的情况了return this.service.findById(id).get();}@PostMapping@ResponseBodypublic E create(@RequestBody E e) {return this.service.save(e);}@PutMapping("{id}")@ResponseBodypublic E update(@RequestBody E e) {return this.service.save(e);}@DeleteMapping("{id}")@ResponseBodypublic E delete(@PathVariable("id") ID id) {E e = this.service.findById(id).get();this.service.deleteById(id);return e;}
}// 具体的 WebAPI
@RestController
@RequestMapping("/user/")
public class UserController extends AbstractCrudController<UserService, User, Long> {public UserController(UserService service) {super(service);}
}
经过重构可以消减掉 Servcie 和 Controller 中的大量重复代码,使代码更容易维护了。
4. 结尾
关于泛型就简单的说这些了,泛型作为 Java 日常开发中一个常用的知识点,其实还有很多知识点可以供我们挖掘,奈何本人才疏学浅,这么多年工作下来,只积累出来这么点内容。
文末放上示例代码的代码库:
- GitHub入口
- gitee入口
相关文章:
重学Java (一) 泛型
1. 前言 泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话,可以参考下面两个链接 Java 泛型详解The Java™ Tutorial…...
Docker 部署 Redis 服务
拉取最新版本的 Redis 镜像: $ sudo docker pull redis:latest在本地预先创建好 data 目录和 conf/redis.conf 文件。 使用以下命令来运行 Redis 容器: $ sudo docker run -itd --name redis --privilegedtrue -p 6379:6379 -v /home/ubuntu/docker/redis/data:/data -v /ho…...

阿里云产品试用系列-负载均衡 SLB
阿里云负载均衡(Server Load Balancer,简称SLB)是云原生时代应用高可用的基本要素。通过将流量分发到不同的后端服务来扩展应用系统的服务吞吐能力,消除单点故障并提升应用系统的可用性。阿里云SLB包含面向4层的网络型负载均衡NLB…...
drf 对象级权限
drf 对象级权限 Django REST Framework(DRF)提供了对象级别权限(Object-level permissions)来控制特定对象的访问权限。 简单来说:通过视图类中的self.get_object(pk)得到一个obj对象(视图对象),在与requ…...

八大排序(二)--------冒泡排序
本专栏内容为:八大排序汇总 通过本专栏的深入学习,你可以了解并掌握八大排序以及相关的排序算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:八大排序汇总 🚚代码仓库:小小unicorn的代码仓库…...

SmartSQL 一款开源的数据库文档管理工具
建议直接蓝奏云下载安装 蓝奏云下载:https://wwoc.lanzoum.com/b04dpvcxe 蓝奏云密码:123 项目介绍 SmartSQL 是一款方便、快捷的数据库文档查询、导出工具!从最初仅支持 数据库、CHM文档格式开始,通过不断地探索开发、集思广…...
代码随想录算法训练营第56天 | ● 583. 两个字符串的删除操作 ● 72. 编辑距离 ● 动态规划之编辑距离总结篇
文章目录 前言一、583. 两个字符串的删除操作二、72. 编辑距离三、动态规划之编辑距离总结篇总结 前言 一、583. 两个字符串的删除操作 两种思路:1.直接动态规划,求两个字符串需要删除的最小次数 2.采用子序列的和-最长公共子序列。思路一分析如下&#…...
矩阵 m * M = c
文章目录 题1题2 题1 (2023江苏领航杯-prng) 题目来源:https://dexterjie.github.io/2023/09/12/%E8%B5%9B%E9%A2%98%E5%A4%8D%E7%8E%B0/2023%E9%A2%86%E8%88%AA%E6%9D%AF/ 题目描述: (没有原数据,自己生成的数据) from Crypto.Util.number…...

Linux——IO
✅<1>主页::我的代码爱吃辣 📃<2>知识讲解:Linux——文件系统 ☂️<3>开发环境:Centos7 💬<4>前言:是不是只有C/C有文件操作呢?python,java&…...

svn(乌龟svn)和SVN-VS2022插件(visualsvn) 下载
下载地址: https://www.visualsvn.com/visualsvn/download/...

开源日报 0824 | 构建UI组件和页面的前端工作坊
Storybook 是一个用于构建 UI 组件和页面的前端工作坊,支持多种主流框架,提供丰富的插件,具有可配置性强和扩展性好的特点。 storybookjs/storybook Stars: 79.9k License: MIT Storybook 是一个用于构建 UI 组件和页面的前端工作坊&#x…...

福建三明大型工程机械3D扫描工程零件三维建模逆向抄数-CASAIM中科广电
高精度3D扫描技术已经在大型工件制造领域发挥着重要作用,可以高精度高效率实现全尺寸三维测量,本期,我们要分享的应用是大型工程机械3D扫描案例。 铣轮是深基础施工领域内工法先进、技术复杂程度高、高附加值的地连墙设备,具有成…...

使用香橙派学习 Linux的守护进程
Q:什么是守护进程 A:Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行 某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个…...
数据治理-数据仓库和商务智能
数据仓库的作用 减少数据冗余,提高信息一致性,让企业能够利用数据做出更优决策的方法,数据仓库是企业数据管理的核心。 业务驱动因素 运营支持职能、合规需求(历史数据响应)和商务智能活动(主因࿱…...

CH2--x86系统架构概览
2.1 OVERVIEW OF THE SYSTEM-LEVEL ARCHITECTURE 图中的实线箭头表示线性地址,虚线表示段选择器,虚线箭头表示物理地址 2.1.1 Global and Local Descriptor Tables 全局描述符表 (GDT) GDT是一个全局的段描述符表,它存储在系统内存中的一个固…...

Immutable.js API 简介
Immutable-js 这个库的实现是深拷贝还是浅拷贝?immutable 来源immutable.js三大特性: 持久化数据结构结构共享惰性操作 Immutable.js 的几种数据类型 immutable 使用 使用 npm 安装 immutable: 常用API介绍 MapListList.isList() 和 Map.isMa…...
HLSL 入门(一)
HLSL High Level Shader Language 高级着色语言,是Direct3D中用来编写Shader的语言。其语法类似于C语言。 虽然其主要作用是用来编写例如顶点着色器,像素着色器。但本质是对图形并行管线进行编程,因此也能用来编写用于计算的着色器ÿ…...
【Docker】挂载数据卷
一、Docker数据卷说明及操作 在Docker中挂载数据卷是一种将数据持久化保存的方法,以便容器之间或容器与主机之间共享数据。以下是如何在Docker中挂载数据卷的步骤: 1、创建数据卷 首先,您需要创建一个数据卷。可以使用以下命令创建一个数据卷…...
[技术干货]spring 和spring boot区别
Spring 和 Spring Boot 都是 Java 框架,用于构建企业级应用程序。Spring 是一个完整的框架,提供各种功能,包括依赖注入、事务管理、数据访问、Web 开发等。Spring Boot 是一个基于 Spring 的框架,旨在简化 Spring 应用程序的开发和…...

【hudi】数据湖客户端运维工具Hudi-Cli实战
数据湖客户端运维工具Hudi-Cli实战 help hudi:student_mysql_cdc_hudi_fl->help AVAILABLE COMMANDSArchived Commits Commandtrigger archival: trigger archivalshow archived commits: Read commits from archived files and show detailsshow archived commit stats: …...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...