重温经典《Thinking in java》第四版之第八章 多态(四十三)

简介: 重温经典《Thinking in java》第四版之第八章 多态(四十三)

在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。

多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序——即无论在项目最初创建时还是在需要添加新功能时都可以“生长”的程序。

“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。这种类型的组织机制对那些拥有过过程化程序设计背景的人来说,更容易理解。而多态的作用则是消除类型之间的耦合关系。在前一章中,我们已经知道,继承允许将对象视为它自己本身的类型或其基类型来加以处理。这种能力极其重要,因为它允许将多种类型视为同一类型来处理,而同一份代码也就可以毫无差别地运行在这些不同类型之上了。多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要它们都是从同一基类导出而来的。这种区别是根据方法行为的不同而表示出来的,虽然这些方法都可以通过同一基类来调用。

 

8.1 再论向上转型

我们已经知道,对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。而这种把对某个对象的引用视为对其基类型的引用的做法被称为向上转型。

publicenumNote { 
MIDDLE_C, C_SHARP, B_FLAT; // Etc. } ///:~ classInstrument { 
publicvoidplay(Noten) { 
print("Instrument.play()"); 
    } 
} 
///:~ //: polymorphism/music/Wind.java packagepolymorphism.music; 
// Wind objects are instruments // because they have the same interface: publicclassWindextendsInstrument { 
// Redefine interface method: publicvoidplay(Noten) { 
System.out.println("Wind.play() "+n); 
    } 
} 
classStringedextendsInstrument { 
publicvoidplay(Noten) { 
print("Stringed.play() "+n); 
    } 
} 
classBrassextendsInstrument { 
publicvoidplay(Noten) { 
print("Brass.play() "+n); 
    } 
} 
///:~ //: polymorphism/music/Music.java // Inheritance & upcasting. packagepolymorphism.music; 
publicclassMusic { 
publicstaticvoidtune(Instrumenti) { 
// ... i.play(Note.MIDDLE_C); 
    } 
publicstaticvoidmain(String[] args) { 
Windflute=newWind(); 
tune(flute); // Upcasting     } 
}   

 

/* Output:

Wind.play() MIDDLE_C

*///:~

请观察一下tune()方法:

publicstaticvoidtune(Instrumenti) { 
// ... i.play(Note.MIDDLE_C); 
}

它接受一个Instrument引用,那么在这种情况下,编译器怎样才能知道这个Instrument引用指向的是Wind对象,而不是Brass对象或Stringed对象呢?实际上编译器无法得知。为了深入理解这个问题,有必要研究一下绑定这个话题。

 

8.2.1 方法调用绑定

将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定(如果有的话,有编译器和连接程序实现),叫做前期绑定。

上述程序之所以令人迷惑,主要是因为前期绑定。因为,当编译器只有一个Instrument引用时,它无法知道究竟调用哪个方法才对。

解决的方法就是后期绑定,它的含义就是在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定或运行时绑定。如果一个语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用恰当的方法。也就是说,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。后期绑定机制随编程语言的不同而有所不同,但是只要想一下就会知道,不管怎么样都必须在对象中安置某种“类型信息”。

Java中除了static方法和final方法(private方法属于final方法)之外,其他所有方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定——它会自动发生。

为什么要将某个方法声明为final方法呢?正如前一章提到的那样,它可以防止其他人覆盖该方法。但更重要的一点或许是:这样做可以有效地“关闭”动态绑定,或者说,告诉编译器不需要对其进行动态绑定。这样,编译器就可以为final方法调用生成更有效的代码。然而,大多数情况下,这样做对程序的整体性能不会有什么改观。所以最好根据设计来决定是否使用final,而不是出于试图提高性能的目的来使用final。

8.2.2 产生正确的行为

一旦知道Java中所有的方法都是通过动态绑定实现多态这个事实以后,我们就可以编写只与基类打交道的程序代码了,并且这些代码对所有的导出类都可以正确运行。或者换一种说法,发送消息给每个对象,让该对象去断定应该做什么事。

面向对象程序设计中,有一个经典的例子就是“几何形状”(shape)。因为它很直观,所以经常用到;但不幸的是,它可能使初学者认为面向对象程序设计仅适用于图形化程序设计,实际当然不是这样。

在“几何形状”这个例子中,有一个基类Shape,以及多个导出类——如Circle、Square、Triangle等。这个例子之所以好用,是因为我们可以说“圆是一种几何形状”、这种说法也很容易被理解。下面的继承图展示它们之间的关系:

 image.png

向上转型可以像下面这条语句这么简单:

Shape s = new Circle();

这里,创建一个Circle对象,并把得到的引用立即赋值给了Shape,这样做看似错误;但实际上没有问题,因为通过继承,Circle就是一种Shape。因此,编译器认可这条语句,也就不会产生错误信息。

假设你调用一个基类方法(他已在导出类中被覆盖):

s.draw();

你可能在此认为调用的是Shape的draw(),因为这毕竟是一个Shape引用,那么编译器是怎样知道去做其他的事情呢?由于后期绑定(多态),还是正确调用了Circle.draw()方法。

 

8.2.3 可扩展性

由于有多态机制,我们可以根据自己的需求对系统添加人一多的新类型,而不需要修改方法。在一个设计良好的OOP程序中,大多数或者所有的方法都会遵循多态模型,只与基类接口同学。这样的程序是可扩展的,因为可以从通用的积累继承出新的数据类型,从而新添一些功能。那些操作基类接口的方法不需要任何改动就可以应用于新类。

目录
相关文章
|
4月前
|
算法 Java 程序员
在Java的编程世界里,多态不仅仅是一种代码层面的技术,它是思想的碰撞,是程序员对现实世界复杂性的抽象映射,是对软件设计哲学的深刻领悟。
在Java的编程世界里,多态不仅仅是一种代码层面的技术,它是思想的碰撞,是程序员对现实世界复杂性的抽象映射,是对软件设计哲学的深刻领悟。
74 9
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第10天】Java零基础教学篇,手把手实践教学!
33 4
|
2月前
|
Java 编译器 程序员
Java多态背后的秘密:动态绑定如何工作?
本文介绍了Java中多态的实现原理,通过动态绑定和虚拟方法表,使得父类引用可以调用子类的方法,增强了代码的灵活性和可维护性。文中通过具体示例详细解析了多态的工作机制。
66 4
|
3月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
2月前
|
Java
java继承和多态详解
java继承和多态详解
52 5
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第1天】Java零基础教学篇,手把手实践教学!
30 1
|
3月前
|
Java 编译器
Java——类与对象(继承和多态)
本文介绍了面向对象编程中的继承概念,包括如何避免重复代码、构造方法的调用规则、成员变量的访问以及权限修饰符的使用。文中详细解释了继承与组合的区别,并探讨了多态的概念,包括向上转型、向下转型和方法的重写。此外,还讨论了静态绑定和动态绑定的区别,以及多态带来的优势和弊端。
80 9
Java——类与对象(继承和多态)
|
2月前
|
安全 Java 编译器
【一步一步了解Java系列】:重磅多态
【一步一步了解Java系列】:重磅多态
24 3
|
3月前
|
Java
Java 多态趣解
在一个阳光明媚的午后,森林中的动物们举办了一场别开生面的音乐会。它们组成了一支乐队,每种动物都有独特的演奏方式。通过多态的魅力,狗、猫和青蛙分别展示了“汪汪”、“喵喵”和“呱呱”的叫声,赢得了观众的阵阵掌声。熊指挥着整个演出,每次调用 `perform()` 方法都能根据不同的动物对象唤起对应的 `makeSound()` 方法,展现了 Java 多态性的强大功能,让整场音乐会既有趣又充满表现力。
31 1
|
4月前
|
安全 Java 编译器