网站首页 > java教程 正文
有些人认为多态是面向对象语言中最核心和最强大的特征。因为对于封装来说,C语言中的接口文件和实现文件分离,其实相对Java封装性更好;对于继承来说,Java的单一继承相对于C++的多重继承,功能上也削弱了一些。而有些人认为多态仅仅是继承机制带来的一点便利而已。以上两种观点都有些低估了多态,多态代表了一种深邃的思想,其影响也早已跃出了语言的边界。那么,多态究竟是什么呢?
在本文中,笔者将围绕以下三个问题展开对多态的深入探讨,去揭开隐藏在多态之上的那层面纱。
问题1:多态是什么?
问题2:多态中蕴含了什么思维模型?
问题3:多态的本质是什么?
01
多态是什么
在本节中,首先介绍一下Java语言中对于多态的定义。然后,看一下Java语言中对于多态的实现。最后,再看列举一些现实生活中与多态类似的类比案例。
1.多态的定义
在Java中,多态(Polymorphism)指的是同一类型的对象可以表现出不同的行为。具体来说,多态性允许父类引用变量指向子类对象,并根据实际运行时类型确定调用哪个方法。这种特性使得代码更加具有可扩展性。
从上面的定义中可以看出,多态的最主要目标是实现代码的可扩展性。
在前面讲封装和继承时提到,它们分别是实现信息隐藏和代码复用的方式之一。同样,可扩展性也是一种目标、结果或是原则,而多态是实现这个目标的方式之一。
下面也让我们来回顾一下,Java语言中有其他哪些同样可以实现可扩展性的方式,下面简单列举一下。
1)泛型编程:使用泛型可以编写通用代码,并且支持类型参数化,在不改变原有结构的基础上拓展新功能或处理不同数据类型;
2)反射+配置:利用Java反射机制动态获取类信息并调用方法,能够在运行时对类进行操作并达到一定程度上的灵活性;
3)中间层解耦:通过引入一个中间层,将直接交互的双方隔离,这样双方可以独立扩展,如消息中间件、代理等都是基于这个思想;
4)依赖注入:通过依赖注入框架(如Spring)管理对象之间的依赖关系,将对象创建与对象使用解耦,在不修改源码情况下动态替换或增加新功能;
5)插件化:使用插件机制允许在运行时加载、卸载和更新软件组件,使得系统可以方便地添加新功能或进行定制化配置。
在上面5个方案中,实际上后面2个大都基于的思想也是多态机制实现的。
2.Java中多态的实现
下面是Java中关于多态的三种实现机制的DEMO示例。
-------------子类继承父类-------------------- //父类Animal class Animal { String name; public Animal(String name) { this.name = name; } public void eat() { System.out.println(name + " is eating."); } } //子类Dog继承自Animal class Dog extends Animal { public Dog(String name) { super(name); } public void bark() { System.out.println(name + " is barking."); } } ----------------接口类和实现类--------------- // 定义一个接口Animal接口 interface Animal { void eat(); void sleep(); } // 实现类Dog实现Animal接口 class Dog implements Animal { @Override public void eat() { System.out.println("Dog is eating."); } @Override public void sleep() { System.out.println("Dog is sleeping."); } } --------------抽象类和实现类--------------------- // 定义一个抽象类Animal abstract class Animal { String name; public Animal(String name) { this.name = name; } // 抽象方法eat,子类必须实现 public abstract void eat(); // 非抽象方法sleep,子类可以选择是否重写 public void sleep() { System.out.println(name + " is sleeping."); } } // 实现类Dog继承自Animal class Dog extends Animal { public Dog(String name) { super(name); } @Override public void eat() { System.out.println(name + " is eating."); } } |
可以看到,多态不一定通过继承来实现。在讲继承的时候,我们将继承关系中所有涉及的类称作一个“家族”,这对于接口和实现类,以及抽象类和实现类同样适用。
因此,可以说多态是通过一个“家族”内的多个层次实现的。这个“家族”的概念对于理解设计模式会特别有用,可以关注笔者后续关于设计模式的讲解。
3.多态的类比案例
在现实生活中,也处处存在着多态思想的运用案例。例如,插座和各类电器的充电线,就是多态的一个好的例子。如果插座和充电器不匹配,中间增加一个转换代理,同样也可以实现。
再比如,我们熟悉的计算机中,也有很多类似的应用,如USB等接口与设备之间、硬件与软件之间、操作系统与硬件之间均是同样的思想。在后两种实现中,也是通过一个中间层来实现的,但是本质思想上并无不同。
02
多态中蕴含了什么思维模型
在本节中,我们进一步对多态的定义进行剖析,并进而推导得出一个多态中蕴含的思维模型。
1.对多态定义的进一步剖析
在多态的定义中,除了可扩展性这个关键字之外,还有另一个语句值得关注:同一类型的对象可以有不同的行为。
在这句话中,体现出一种静态和动态的对立。其中,静态部分要求类型保持不变,而动态部分则要求行为可变。
我们可以先将java语言中多态的几个实现机制(继承、接口和抽象类)抛之脑后,仅仅保留一个客体。在这个初始条件下,多态本质上是要求这个客体要在一个静态中同时保持着变化的能力。那么,应该如何实现呢?
答案是客体必须先实现自身的分离,将变化和不变的部分拆分开,这是多态实现的基础,也是Java语言中不论是继承、接口还是抽象类所采用的策略。
然后,我们再来将客体放在一个主客体交互的环境中,来看一种主客体控制变化的思维模型。
2.一种主客体控制变化的思维模型
主客体控制变化,意味着尽可能将变化控制在最小的范围内。我们先来看下面这张图。传统方式下,如果主体要调用客体,需要先依赖客体的类,然后再进行调用。不论是依赖流还是调用流,都是主体指向客体的。
但是传统方式下,有一个明显的问题。如果客体发生变化,比如提供这个组件的厂商倒闭等,作为上游的主体就需要被迫修改。
现在要解决的问题就是,客体主动变化的情况下,如何确保主体不被动变化。当然,如果主体的需求变化,要求客体被动变化,那么主体的这种修改就属于主动修改,是合理的。
我们看看多态的思想是如何解决这一问题的。首先,就是将客体分离成不变的和变化的两部分。
然后,将客体不变的部分和主体物理上放在一起(一起编译、打包),这样依赖流就可以倒转。在这种方式下,客体变化部分可以自行扩展,就和主体不再有关系,因为主体只依赖了客体不变的部分。也就是说,主体和客体两者实现了解耦,双方都可以单独编译、打包和上线部署等。
为了方便理解,下图中增加了一个Client,对Client来说,它如果需要用到客体扩展的类,它是需要修改的。
还有一些情况,主体没有办法将客体不变的部分纳入进来,比如主体是一个遗留系统。那么就可以增加一个中间层,让中间层去包含客体不变的部分,比如代理模式和消息模式就是这样一种实现思路,如下图所示。
最后,总结一下,多态最为本质的东西是实现了一种不变与变化的分离,但是这种分离包含两个层面,一是逻辑层面分离,这种分离在继承、接口和抽象类的机制中已经实现;二是物理层面分离,即不变的部分和主体或者中间层一起编译、打包和部署。两种分离结合起来,才最终实现了多态真正可以达到的效果。这种多态的思想不仅在Java语言编程层面应用,在架构层面的解耦中也同样如此,比如架构中的插件架构、事件驱动架构、DDD中的六边形和洋葱架构等,都是运用了相同的思路。
在前面讲到封装和继承所蕴含的思维模型,同时也探讨了其中涉及的哲学思想。多态性所体现的不变与变化分离理念,在哲学领域同样有着诸多渊源。举例来说,笛卡尔关于精神与物质、灵魂与肉体二元对立的观念,与多态性中的分离概念遥相呼应,此处不再深入展开。
03
多态的本质是什么
接下来,我们同样来探讨一下多态的本质。希望对于以下多个观点的讨论,也可以引发读者更多深入的思考。
1.观点1:多态的本质是为了实现可扩展性
该观点认为,多态的本质为了实现程序的可扩展性,如果没有多态能力,程序只能使用if、else等条件语句来硬编码,但是这样程序的扩展性会很差。
2.观点2:多态的本质是一种延迟处理机制
该观点认为,多态的本质是一种延迟处理机制。对于程序来说,编程时无法知道运行时的环境信息,只有在运行时才能获取足够的信息去做决定,多态提供了一种技术让延迟处理得以实现。
3.观点3:多态的本质是一种分离思想
该观点认为,多态的本质体现了一种分离思想,包括了程序中变化和非变化的分离、程序在编译态和运行态时间维度上的分离、程序在运行态空间维度上的分离等,或者说是一种不变和变化部分在逻辑和物理上的分离。
4.观点4:多态的本质是一种控制变化的机制
该观点认为,多态的本质是一种控制变化的机制,主要通过将变化和非变化的分离,以及控制依赖关系的方向来进行实现。
猜你喜欢
- 2024-09-27 Java面向对象的三大特性之——多态
- 2024-09-27 Java语言的特点有哪些?你对Java认知有多少?
- 2024-09-27 大厂面试系列-详解Java面向对象的三大基本特征
- 2024-09-27 Java的这五大特点你都不知道,还敢说自己是程序猿?
- 2024-09-27 Java入门学习:快速了解Java语言的11个特点
- 2024-09-27 细说JAVA的特点,快速带你走近Java的世界
- 2024-09-27 理解Java的三大特性之封装(java封装的定义)
- 2024-09-27 Java语言的11大特性-你知道吗?(java语言的11大特性-你知道吗?怎么回答)
- 2024-09-27 嗨,你知道吗,Spring还有这些高级特性
- 2024-09-27 java不可不知系列——java的11个特点,为什么要选择java?
你 发表评论:
欢迎- 05-16SpringBoot整合Redis实现常用功能
- 05-16基于Redis实现简单的延时消息队列
- 05-16安装Redis
- 05-16Spring系列之Redis的两种集成方式
- 05-16Django连接Redis集群问题排查思路和总结
- 05-16只需5分钟,完成Redis所有命令操作~
- 05-16熟练使用 Redis 的 5 大数据结构:Java 实战教程
- 05-16Redis 常见业务场景及实例(Java)
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)