深入浅出泛型,框架设计的基础 什么是框架思路
yuyutoo 2024-10-20 13:08 4 浏览 0 评论
泛型在 Java 5 出现,实现了参数化类型,主要作用是使得类或接口更加通用。比如 Java 中的容器类,通过泛型实现了对各种类型的兼容,成为极其通用的类库。如果我们要设计自己的框架,泛型基本上已经算是标配了。
类使用泛型
在类型上使用泛型,非常简单,只需要使用 <> 声明类型即可。如下 TestDO 类型声明了泛型 T,并使用 T 声明了字段 value。这样做的好处便是,TestDO 可以承载各种各样的数据,而不用定义具体的类型。这也说明了,泛型不仅可以用于类型,也可以用于字段或变量。
Java 在运行时对泛型进行擦除,运行时所有的泛型类型都退化为 Object 类型。这样的设计主要是为了保证对 Java 5 之前的代码进行兼容。尽管如此,使用泛型后,在编译阶段仍会对类型进行检查,确保使用泛型的地方都是类型正确的。
@Getter
@Setter
public
classTestDO{Tvalue;}
如下代码中,SubTestDO 传递给父类的具体类型为默认类型 Object 类型,而SubTestDOSimple 传递给父类的具体类型则是 String 类型。这两个子类都对泛型做了具体化,是泛型类型的常规使用。
/** 无泛型,即为 Object */
classSubTestDOextendsTestDO{}
/** 继承时指定父类实际类型 */
classSubTestDOSimpleextendsTestDO{}
在定义 TestDO 的子类时,子类也可以使用泛型。子类的泛型可以独自使用,也可以传递给父类。如 SubTestDOTrans 定义了泛型 T 并把 T 传递给父类,而 SubTestDOComp 定义了泛型 T 要求必须是 HashMap 的子类,但传递给父类的泛型变成了 List。
/** 继承时传递泛型 */classSubTestDOTransextendsTestDO{}
/** 继承时传递复杂类型 */
publicclassSubTestDOCompextendsSubTestDOTrans>{}
接口使用泛型可以定义适用于各种场景的通用接口,在设计模式中具有重要作用。如工厂模式就可以实现根据返回值类型自动转型,涉及到方法的泛型,在后文给出解释。
如下代码中,IGeneric 接口使用了泛型,并用泛型定义了方法。如此定义后,IGeneric 接口成为了一个通用接口,方法的入参类型与实现类或子接口对接口的定义保持一致,大大提高了通用性。接口可以继承,在继承中泛型可以有形式的变化,并定义新的泛型。
/** 普通泛型接口,泛型 T */
publicinterfaceIGeneric{publicvoidtest(Tt);}
ISubGenericSimple 接口在继承接口时,直接确定了泛型的类型为 String 类型,ISubGenericSimple 接口退化为一个普通接口,子接口或实现类不再需要对泛型进行处理。这是通过子接口对泛型进行具体化,也是架构设计中的常用手段。
/** 继承时指定泛型 */
interfaceISubGenericSimpleextendsIGeneric{}
ISubGenericTrans 接口同样使用了泛型,并把泛型传递给父接口。这种一般用于父接口的扩展,在子接口中可以定义一些特有方法,同时又完全兼容父接口的引用。
/** 继承时传递泛型 */
interfaceISubGenericTransextendsIGeneric{}
ISubGenericSubType 接口在泛型定义中,明确声明了泛型类型的范围必须是 TestDO 及其子类,这也是对父接口的一种退化,缩小了泛型的范围。这种情况一般用于比较复杂的接口体系,父接口完成顶层定义,多个子接口进行类型具体化实现分层设计。
/** 继承时指定泛型为特定类型的子类 */
interfaceISubGenericSubTypeextendsIGeneric{}
ISubGenericCompType 接口使用了一个相对复杂的泛型定义。子接口定义了泛型 T,而传递给父接口的泛型变成了 TestDO。这种接口继承方式在大型软件架构中有比较广泛的应用,通俗理解为父接口使用容器但元素类型为泛型,而子接口定义容器的元素类型,在组件化继承体系有比较明确的应用。这样的继承关系,可以确保父接口完成容器的操作方法而不用关心具体元素类型,子接口可以有少许定制逻辑甚至可以不定义方法。
/** 继承时指定复杂的泛型类型 */
interfaceISubGenericCompTypeextendsIGeneric>{}
方法使用泛型
方法也可以使用泛型,并且有很大的现实意义,在工厂模式、建造器模式中都可以使用泛型方法。
toString 方法定义了一个简单的泛型方法。在访问权限和返回值类型之间使用 声明泛型类型为 T,然后把入参类型设置为 T。在这个泛型方法中,入参类型可以是任何类型,这个通用方法可以在任何场景下使用。
/** 泛型 T 仅用于入参 */
staticStringtoString(Tt){returnnull;}
getInstance 方法不但在入参中使用了泛型,也把这个泛型作为返回值的类型。类似 getInstance 方法的定义有很多,比如 Spring 的 getBean 方法就重载了这种形式的应用。在确保方法通用性的基础上,也保证了返回值类型与入参的一致性。
/** 返回 T 类型 */
staticTgetInstance(Classclazz)throwsException{returnclazz.newInstance();}
getTestDO 方法是另一种形式的泛型方法,这里定义了返回值的泛型必须是 TestDO 及其子类。这种泛型方法常用于继承体系明确,需要对类型进行向下转型的场景。在方法内部需要对返回值进行强制转型,如果类型不匹配便会抛出类型转换异常,因此要求调用者必须了解方法的定义和使用要求。
/** 返回 TestDO 的子类类型 */
staticTgetTestDO(){return(T)newSubTestDOTrans<>();}
下面 getTestDO 的重载方法对泛型做了一定程度的退化,虽然定义了泛型 T,但这个泛型仅仅是容器的元素类型,方法的返回值是 TestDO。这种类型的泛型方法可用于复杂对象的组装,比上面的重载方法更安全。
staticTestDOgetTestDO(Tv){TestDOtestDO=newTestDO<>();testDO.setValue(v);returntestDO;}
泛型擦除
前面提到过 Java 会在运行时对泛型进行擦除,也就是抹去泛型信息,全部变为 Object 类型。因此,在运行时,TestDO 与 TestDO 在类型上并没有多大区别。虽然实际存储的 value 对象的真正类型是不同的,但是都会当做 Object 进行处理。也是因为擦除,引用类型对象时,我们可以使用 TestDO.class 却不能使用 TestDO.class。
泛型通配
前面的泛型都明确了泛型的类型为 T,并使用 T 定义字段、参数和返回值。在对具体类型依赖不那么强烈的情况下,比如,我们仅仅是要求类型是某个类型的子类即可,最终处理时使用父类类型而不依赖于子类类型,则可以使用通配符 ? 来定义泛型。
在如下代码中,set1 的元素类型为 TestDO 的子类。我们只关心 Set 中的元素是 TestDO 类型即可,并不关心实际元素是 SubTestDOSimple 还是 SubTestDOComp。而 set2 的元素类型可以是任何类型。
Setset1=newHashSet<>();
Setset2=newHashSet<>();
泛型逆变
前面提到的泛型定义用到了 extends 来确定泛型的边界,要求其必须是某个类型的子类。实际上,泛型还支持向上确定边界,也就是泛型为某个类型的父类。在如下代码中,定义了 Set 的元素类型为 SubTestDOComp 的父类。
SettestDOs=newHashSet<>();
使用泛型创建类型安全的分层结构
在架构设计中,常常涉及复杂的数据结构与设计分层,使用泛型对类型进行定义和约束,可以确保抽象层的设计有足够的通用性,同时最终产生的具体类型依然保持正确的数据类型。
比如在一个架构设计中,可能会分为抽象层、基础层、实现层三个层次。
在抽象层定义极度抽象的业务流程,需要这里的类型定义都具有较好的通用性,这时可以大量使用泛型和抽象类,只定义流程而不涉及具体类型。
在基础层定义一些基础服务,可能会在 core 层类型的基础上做一定程度的扩展,封装出多个适用范围不同的业务处理流程。在这一层,也会使用泛型,但会更加具体。
而在实现层不会再使用泛型,而是给出真正的类型,并实现具体的逻辑。
/** 抽象层,指定泛型 K,V */
abstractclassAbstractGenericImplimplementsIGeneric>{publicMapmap=newHashMap<>();}
/** 基础层,继承 AbstractGenericImpl */
classBasicGenericImplextendsAbstractGenericImpl>{@Overridepublicvoidtest(Map>data){map.putAll(data);}}
/** 实现层,继承 BasicGenericImpl */
publicclassComplexGenericImplextendsBasicGenericImpl{@Overridepublicvoidtest(Map>data){super.test(data);}}
总结
泛型在当今时代的 Java 开发中已经被广泛应用,如 Spring、MyBatis、Dubbo 等开源框架都使用了大量泛型来实现通用功能。在开发中,既要掌握开源框架的泛型使用方法,也要熟悉泛型的定义和使用,并对泛型有足够深刻的理解。如果你要开发自己的框架,那么泛型一定是标配,需要好好理解和掌握。
相关推荐
- YAML配置文件简介及使用(yaml 配置)
-
简介YAML是"YAMLAin'taMarkupLanguage"(YAML不是一种标记语言)的缩写。相比JSON格式的方便。...
- 教你如何解决最常见的58种网络故障排除方法
-
1.故障现象:网络适配器(网卡)设置与计算机资源有冲突。分析、排除:通过调整网卡资源中的IRQ和I/O值来避开与计算机其它资源的冲突。有些情况还需要通过设置主板的跳线来调整与其它资源的冲突。2.故障现...
- 一分钟带你了解服务器网卡(服务器网卡怎么用)
-
今天小编和大家聊一下服务器的网卡。什么是网卡?简单说网卡就是计算机与局域网互连的设备。计算机主要通过网卡接入网络。网卡又称为网络适配器或网络接口卡NIC(NetworkinterfaceCard)...
- linux文件之ssh配置文件的含义与作用
-
ssh远程登录命令是操作系统(包括linux和window系统)下常用的操作命令,可以帮助用户,远程登录服务器系统,查看,操作系统相关信息。linux系统对于ssh命令有专门保存其相关配置的目录和文件...
- Cilium 官方文档翻译 - IPAM(二)Kubernetes Host模式
-
KubernetesHostScopeciliumIPAM的kuberneteshost-scope模式通过选项ipam:kubernetes开启,将集群IP地址分配委托给每个独立的节点,并...
- 域名劫持跳转,域名劫持跳转的解决办法只需5步
-
简单来说,域名劫持就是把原本准备访问某网站的用户,在不知不觉中,劫持到仿冒的网站上,例如用户准备访问某家知名品牌的网上商店,黑客就可以通过域名劫持的手段,把其带到假的网上商店,同时收集用户的ID信息和...
- Linux 磁盘和文件系统管理(linux磁盘管理fdisk)
-
1检测并确认新硬盘...
- windows host文件怎么恢复?局域网访问全靠这些!
-
windowshost文件怎么恢复?windowshost文件是常用网址域名及其相应IP地址建立一个关联文件,通过这个host文件配置域名和IP的映射关系,以提高域名解析的速度,方便局域网用户使用...
- Nginx配置文件详解与优化建议(nginx 配置详解)
-
1、概述今天来详解一下Nginx的配置文件,以及给出一些配置建议,希望能对大家有所帮助。...
- Mac电脑hosts文件锁定,如何修改hosts文件权限
-
有时候我们需要修改hosts文件,但是网上很多教程都行不通,使用sudo命令也不行。其实有一个很简单的方法。打开终端命令行,使用如下命令即可:sudochflags-hvnoschg/etc/...
- windows电脑如何修改hosts文件?(windows 修改hosts文件)
-
先来简单说下电脑host的作用hosts文件的作用:hosts文件是一个用于储存计算机网络中各节点信息的计算机文件;作用是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中...
- Vigilante恶意软件行为怪异:修改Hosts文件以阻止受害者访问盗版网站
-
Sophos刚刚报道了一款名叫Vigilante的恶意软件,但其行为却让许多受害者感到不解。与其它专注于偷密码、搞破坏、或勒索赎金的恶意软件不同,Vigilante会通过修改Hosts文件...
- hosts文件无法修改几种现象和解决方法
-
第一种、hosts文件修改完不是直接保存而是弹出另存为窗口解决:1、右击hosts文件——属性——把“只读”前面勾去掉。第二种、打开hosts文件时提示“你没有权限打开该文件,请向文件的所有者或管理员...
- hosts文件位置在哪里,教你hosts文件位置在哪里
-
Hosts是一个没有扩展名的系统文件,其基本作用就是将一些常用的网址域名与其对应的IP地址建立一个关联"数据库",当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的I...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
【开源分享】2024在线客服系统PHP源码(安装教程+全新UI)
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)