专业的JAVA编程教程与资源

网站首页 > java教程 正文

面向对象Java语言三大特征之多态(什么是面向对象的多态性,在程序中如何体现的?)

temp10 2024-09-27 02:58:28 java教程 8 ℃ 0 评论

有些人认为多态是面向对象语言中最核心和最强大的特征。因为对于封装来说,C语言中的接口文件和实现文件分离,其实相对Java封装性更好;对于继承来说,Java的单一继承相对于C++的多重继承,功能上也削弱了一些。而有些人认为多态仅仅是继承机制带来的一点便利而已。以上两种观点都有些低估了多态,多态代表了一种深邃的思想,其影响也早已跃出了语言的边界。那么,多态究竟是什么呢?

在本文中,笔者将围绕以下三个问题展开对多态的深入探讨,去揭开隐藏在多态之上的那层面纱。

面向对象Java语言三大特征之多态(什么是面向对象的多态性,在程序中如何体现的?)

问题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:多态的本质是一种控制变化的机制

该观点认为,多态的本质是一种控制变化的机制,主要通过将变化和非变化的分离,以及控制依赖关系的方向来进行实现。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表