接口多态与方法多态
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
在上一篇设计山寨版Stream API时,有一个技巧被频繁使用:接口多态。
接口,用的是函数式接口,即接口内部有且仅有一个抽象方法。
多态,原本指的是接口下有多个子类实例可以指向接口引用,但由于函数式接口恰好仅有一个方法,此时接口多态等同于“方法多态”,即一个抽象方法拥有多个不同的具体实现。
接口多态
我们都知道Java是面向对象的语言,它具备多态性。私以为,多态的精髓在于晚绑定。什么意思呢?
PocketMon pocketMon = new Pikaqiu();
pocketMon.releaseSkill();
只看pocketMon.releaseSkill()你能猜出来技能是电击还是喷火吗?
哦?一眼就看出来了?
这样呢?
Properties pro = new Properties();
FileInputStream in = new FileInputStream("pocketmon.properties");
pro.load(in);
PocketMon pocketMon = Class.forName(pro.getProperty("nextPocketMon")).newInstance();
pocketMon.releaseSkill();
完全看不出来了。
即使你打开pocketmon.properties看了是皮卡丘,运行时虚拟机看到的可能是我修改后的喷火龙。
这种现象其实很奇妙:明明代码都写死了,但虚拟机却无法提前确定具体会是哪只神奇宝贝在调用releaseSkill(),除非实际运行到这行代码。而这,正是得益于多态。
多态的原理,本质是还是JVM层面通过运行时查找方法表实现的。可以简单理解为,JVM在运行时需要去循环遍历这个方法对应的多态实现,选择与当前运行时对象匹配的方法进行调用。所以,从理论上来说,晚绑定的多态在性能上是不如早绑定的(直接写死,不用多态)。而多态是设计模式的灵魂,所以对于一些非常、非常、非常要求性能的场景来说,过于繁重的设计反而会降低性能。说白了,这世上就不存在多、快、好、省。
多态是“晚绑定”思想的体现:对于Java而言,方法的调用并不是编译时绑定,而是运行时动态绑定的,取决于引用具体指向的实例。
方法多态
我生造了“方法多态”这个概念,但这个概念在函数式接口的前提下是站得住脚的,而且有利于跳出面向对象,贴近函数式编程。
我们来看一个需求:
要求写一个cook()方法,传入鸡翅和可乐,你给我做出可乐鸡翅。
很多人可能下意识地就把代码写死了:
public static CokaChickenWing cook(Chicken chicken, Coka coka){1.放油、放姜;2.放鸡翅;3.倒可乐;4.return CokaChickenWing;
}
但是,网上也有人说应该先倒可乐再放鸡翅,每个人的口味不同,做法也不同。有没有办法把这两步延迟确定呢?让调用者自己来安排到底是先倒可乐还是先放鸡翅。
可以这样:
public static CokaChickenWing cook(Chicken chicken, Coka coka, function twoStep){1.放油、放姜;2~3.twoStep();4.return CokaChickenWing;
}
想法很好:既然这两步不确定,那么就由调用者来决定吧,让调用者自己传进来。
我们知道Java是不能直接传递方法的,但利用策略模式可以解决这个问题。
定义一个接口:
interface TwoStep {void excute();
}
然后呢?
public static CokaChickenWing cook(Chicken chicken, Coka coka, TwoStep twoStep){1.放油、放姜;2~3.twoStep.excute();4.return CokaChickenWing;
}
这里twoStep.excute()是确定的吗?
没有。
你说它是先倒可乐,再放鸡翅?我偏要说它是先放鸡翅,再倒可乐!反正接口也没方法体,具体实现要看你传进来什么对象。
所以twoStep.excute()充其量只是先替“某些操作占个坑”,后面再确定。
什么时候确定呢?
main(){TwoStep twoStep = new TwoStep(){@Overridepublic void excute(){2.先放鸡翅3.再倒可乐}}// 调用cook时确定(运行时)cook(chicken, coka, twoStep);
}public static CokaChickenWing cook(Chicken chicken, Coka coka, TwoStep twoStep){1.放油、放姜;2~3.twoStep.excute();4.return CokaChickenWing;
}
来,学过Lambda表达式后,我们换个时髦的写法:
main(){// 调用cook时确定 方案1cook(chicken, coka, (鸡翅, 可乐) -> 2.先放鸡翅,3.再倒可乐);// 调用cook时确定 方案2cook(chicken, coka, (鸡翅, 可乐) -> 2.先倒可乐,3.再放鸡翅);
}public static CokaChickenWing cook(Chicken chicken, Coka coka, TwoStep twoStep){1.放油、放姜;2~3.twoStep.excute();4.return CokaChickenWing;
}
这就是我所谓的“方法多态”:通过函数式接口把形参的坑占住,后续传入不同的Lambda实现各自逻辑。
晚绑定与模板方法模式
在设计模式中策略模式和模板方法看起来有点像,但其实不一样。策略模式使用接口占坑,然后传入实际对象调用需要的方法,而模板方法模式是用抽象方法占坑,粒度其实小一些。
晚绑定最典型的应用就是模板方法模式:抽象类确定基本的算法骨架,把不确定的、变化的部分做成抽象方法剥离出去,由子类来实现。
还是以发送验证码为例:
/*** 验证码发送器** @author mx*/
public abstract class AbstractValidateCodeSender {/*** 生成并发送验证码*/public void sendValidateCode() {// 1.生成验证码String code = generateValidateCode();// 2.把验证码存入Session// ....// 3.抽象方法占坑,用于发送验证码sendCode();}/*** 具体发送逻辑,留给子类实现:发送邮件、或发送短信都行*/protected abstract void sendCode();/*** 生成验证码** @return*/public String generateValidateCode() {return "123456";}}
对于上面的模板,我们可以有多种实现方式,以便把sendCode()这个坑填上:
/*** 短信验证码发送** @author mx*/
public class SmsValidateCodeSender extends AbstractValidateCodeSender {@Overrideprotected void sendCode() {// 通过阿里云短信发送}
}
/*** QQ邮箱验证码发送** @author mx*/
public class EmailValidateCodeSender extends AbstractValidateCodeSender {@Overrideprotected void sendCode() {// 通过QQ邮箱发送}
}
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
进群,大家一起学习,一起进步,一起对抗互联网寒冬
相关文章:
接口多态与方法多态
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 在上一篇设计山寨版Str…...
js小技巧|如何提取经过Function函数混淆了的代码
关注它,不迷路。 本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除! 1.需求 星友发过来一个混淆代码,打开一看,长这…...
【GitLab】流水线入门
(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮࿰…...
es 中文前缀短语匹配(搜索智能补全)
需求:es进行前缀匹配,用来进行智能补全 过程:es正常的prefix只能进行词语匹配,而中文的分词大部分按字分词,不按语义分词,所以无法搜索出正确的前缀匹配,而能进行短语匹配的match_phrase_prefix…...
机器学习之决策树及随机森林
决策树 概念 决策树(Decision Tree)是一种常见的机器学习算法,用于分类和回归任务。它是一种树状结构,其中每个内部节点表示一个特征或属性,每个分支代表一个决策规则,而每个叶节点表示一个输出标签或值。 构建决策树过程 构建决策树的过程通常涉及以下步骤: 数据准…...
用通俗的方式讲解Transformer:从Word2Vec、Seq2Seq逐步理解到GPT、BERT
直到今天早上,刷到CSDN一篇讲BERT的文章,号称一文读懂,我读下来之后,假定我是初学者,读不懂。 关于BERT的笔记,其实一两年前就想写了,迟迟没动笔的原因是国内外已经有很多不错的资料࿰…...
数据结构-01-数组
每一种编程语言中,基本都会有数组这种数据类型。不过,它不仅仅是一种编程语言中的数据类型,还是一种最基础的数据结构。 1-数组的概念和特性 数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来…...
甘草书店记: 2023年10月11日 星期三 晴 「做有光的人,照亮他人,也引人同行」
发了两篇《甘草书店记》,书店计划公之于众,收获了不少人的赞扬和鼓励,来自生活中的友人,来自麦田的客户和朋友,来自图书界的同行前辈,也来自商界的同仁。其中,最特别留言来自甘草书店投资方的张…...
让 OpenAI GPT4 出 10 道题测试其他开源大语言模型
让 OpenAI GPT4 出 10 道题测试其他开源大语言模型 1. 中文题目及答案2. 日文题目及答案3. 英文题目及答案 1. 中文题目及答案 数学题:一个矩形的长是10厘米,宽是5厘米,求它的面积。 答案:面积 长 x 宽 10厘米 x 5厘米 50平方厘…...
动态库与静态库
1. 库 是代码的二进制的封装形式 在其他的源代码或库中,可以直接调用库的,但是又看不到它 没有公开源代码 库的这种实现方法有利于模块化 而且只要接口合理 不影响库的使用的 sum.c sum.h int sum(int a,int b) { return ab; } xxx.c 需要使用…...
pdf文件编辑,[增删改查]
pdf文件是投标文件中必不可少的格式,传统的方式先编辑word格式,最后生成pdf,但是有时候需要直接编辑pdf文件,编辑pdf的工具无疑 “adobe acrobat dc”是最好用的之一了 1.把图片文件添加到pdf指定位置,例如把一张图片添…...
如何与LEONI建立EDI连接?
莱尼LEONI是一家为汽车及其他行业提供能源数据管理产品、解决方案及服务的全球供应商。供应链范围从研发生产标准化电缆、特种电缆和数据电缆到高度复杂的布线系统和相关组件。本文将介绍如何与莱尼LEONI建立EDI连接。 什么是EDI? EDI全称Electronic Data Interch…...
算法中的时间复杂度,空间复杂度
一、前言 算法(Algorithm)是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别 衡量不同算法之间的优劣主要是通过时…...
Python基础:推导式(Comprehensions)详解
1. 推导式概念 Python推导式(comprehensions)是一种简洁而强大的语法,用于从已存在的数据(列表、元组、集合、字典等)中创建新的数据结构。推导式包括: 列表推导式元组推导式字典推导式集合推导式 2. 列表…...
安防监控视频融合平台EasyCVR定制化页面开发
安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索…...
Roll-A-Ball 游戏
Roll-A-Ball 游戏 1)学习资料 b站视频教程:https://www.bilibili.com/video/BV18W411671S/文档: * Roll-A-Ball 教程(一), * Roll-A-Ball 教程(二)线上体验roll-a-ball成品 * http://www-personal.umich.e…...
医疗影像数据集—CT、X光、骨折、阿尔茨海默病MRI、肺部、肿瘤疾病等图像数据集
最近收集了一大波关于CT、X光等医疗方面的数据集包含骨折、阿尔茨海默病MRI、肺部疾病等类型的医疗影像数据,废话不多说,给大家逐一介绍!! 1、彩色预处理阿尔茨海默病MRI(磁共振成像)图像数据集 彩色预处理阿尔茨海默病MRI(磁共…...
Linux僵死进程及文件操作
1.僵死进程(僵尸进程): 1.僵死进程产生的原因或者条件: 什么是僵死进程? 当子进程先于父进程结束,父进程没有获取子进程的退出码,此时子进程变成僵死进程. 简而言之,就是子进程先结束,并且父进程没有获取它的退出码; 那么僵死进程产生的原因或者条件就是:子进…...
用Python写一个浏览器集群框架
更多Python学习内容:ipengtao.com 在分布式爬虫和大规模数据采集的场景中,使用浏览器集群是一种有效的方式,可以提高数据采集的速度和效率。本文将介绍如何用Python编写一个简单但强大的浏览器集群框架,以应对需要使用多个浏览器实…...
【Github】git安装
我们经常需要对github上的项目进行复现或者使用,git指令可以方便我们更好地实现他们。 Part 0. 准备 配置代理IP 面对问题:关于登陆github网站网速慢、下载git项目网速慢。 解决:无论是windows还是linux系统,都可以找到/etc/ho…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
