在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本形态。
多态通过分离做什么和怎么做,从另一角度将接口和实现分离出来。多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序——即无论在项目最初创建时还是在需要添加新功能时都可以“生长”的程序。
“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。这种类型的组织机制对那些拥有过程化程序设计背景的人来说,更容易理解。而多态的作用则是消除类型之间的耦合关系。继承允许将对象视作它自己本身的类型或其基类型来加以处理。这种能力极其重要,因为它允许将多种类型视为同一类型来处理,而同一份代码也就可以毫无差别地运行在这些不同类型之上了。
多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要他们都是从同一基类导出而来的。这种区别是根据方法行为的不同而表示出来的,虽然这些方法都可以通过同一基类来调用。
向上转型
对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。一个子类的对象可以用父类的引用去表示。而这种把对某个对象的引用视为对其基类型的引用的做法被称为向上转型——因为在继承树的画法中,基类是放置在上方的。
public enum Node{
MIDDLE_C ,
C_SHARP ,
B_FLAT ;
}
//Music.java
class Instrument{
public void play(Node n){
System.out.println("Instrument.play()");
}
}
class Wind extends Instrument{
//重新定义接口方法
public void play(Node n){
System.out.println("Wind.play()" + n);
}
}
public class Music {
public static void tune(Instrument i){
i.play(Node.MIDDLE_C);
}
public static void main(String[] args){
Wind flute = new Wind();
//向上转型;
tune(flute);
}
}
Music.tune()方法接受一个Instrument引用,同时也接受任何导出自Instrument的类。在main()方法中,当一个Wind引用传递到tune()方法时,就会出现这种情况,而不需要任何类型转换。这样做是允许的——因为Wind从Instrument继承而来,所以Instrument的接口必定存在于Wind中。
8.2产生正确的行为
一旦Java中所有方法都是通过动态绑定实现多态这个事实之后,我们就可以编写只与基类打交道的程序代码了,并且这些代码对所有的导出类都可以正确运行。或者换一种说法,发送消息给某个对象,让对象去判定应该做什么事。
在“几何形状”这个例子中,有一个基类Shape,以及多个导出类——如Circle、Squar、Triangle等。这个例子之所以好用,是因为我们可以说“圆是一种几何形状”
import java.util.Random;
class Shape{
public void draw(){}
public void erase(){}
}
class Circle extends Shape{
public void draw(){
System.out.println("Circle.draw()");
}
public void erase(){
System.out.println("Circle.erase()");
}
}
class Square extends Shape{
public void draw(){
System.out.println("Square.draw()");
}
public void erase(){
System.out.println("Square.erase()");
}
}
class Triangle extends Shape{
public void draw(){
System.out.println("Triangle.draw()");
}
public void erase(){
System.out.println("Triangle.erase()");
}
}
class RandomShapeGenerator{
private Random rand = new Random(47);
public Shape next(){
switch (rand.nextInt(3)){
default:
//向上转型了
case 0 : return new Circle();
case 1 : return new Square();
case 2 : return new Triangle();
}
}
}
public class Shapes {
private static RandomShapeGenerator gen = new RandomShapeGenerator();
public static void main(String[] args){
//shape引用组成的数组
Shape[] s = new Shape[9];
//将数组充满形状
for(int i = 0;i < s.length ; i++){
s[i] = gen.next();
}
//多态方法调用
for(Shape shp : s){
shp.draw();
}
}
}
Shape基类为自它那里继承而来的所有导出类建立了一个公用接口——也就是说,所有形状都可以描绘和擦除。导出类通过覆盖这些定义,来为每种特殊类型的几何形状提供单独的行为。
RandomShapeGenerator是一种“工厂”,在我们每次调用next()方法时,它可以为随机选择的Shape对象产生一个引用。请注意向上转型是在return语句里发生的。每个return语句取得一个指向某个Circle、Square或者Triangle的引用,并将其以Shape类型从next()方法中发送出去。所以无论我们在什么时候调用next()方法时,绝对不可能知道具体类型到底是什么,因为我们总是只能获得一个通用的Shape引用。
当我们遍历这个数组(shape引用组成的数组),并为每个数组元素调用draw()方法时,与类型有关的特定行为就会神奇般的正确发生。
可扩展性
事实上,不需要改动tune()方法,所以的新类都能与原有类一起正确运行。即使tune()方法是单独存放在某个文件中,并且在Instrument接口中添加了其他的新的方法,tune()也不需再编译就能正确运行

enum Node{
MIDDLE_C ,
C_SHARP ,
B_FLAT ;
}
class Instrument{
void play(Node n){
System.out.println("Instrument.play()" + n);
}
String what(){
return "Instrument" ;
}
void adjust(){
System.out.println("Adujusting Instrument");
}
}
class Wind extends Instrument{
void play(Node n){
System.out.println("Wind.play()" + n);
}
String what(){
return "Wind";
}
void adjust(){
System.out.println("Adjusting Wind");
}
}
class Percussion extends Instrument{
void play(Node n ){
System.out.println("Percussion.play()" + n);
}
String what(){
return "Percussion";
}
void adjust(){
System.out.println("Adjusting Percussion");
}
}
class Stringed extends Instrument{
void play(Node n){
System.out.println("Stringed.play()" + n);
}
}
class Brass extends Wind{
void play(Node n ){
System.out.println("Wind.play" + n);
}
void adjust(){
System.out.println("Adjusting Brass");
}
}
class Woodwind extends Wind{
void play(Node n) {
System.out.println("Woodwind.play()" + n);
}
String what(){
return "Woodwind";
}
}
public class Music2 {
//多态
public static void tune(Instrument i){
i.play(Node.MIDDLE_C);
}
public static void tuneAll(Instrument[] e){
for(Instrument i : e){
tune(i);
}
}
public static void main(String[] args){
//添加到数组的时候向上转型
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
新添加的方法what()返回一个带有描述的String引用;另一个新添加的adjust()则提供每种乐器的调音方法。
在main()中,当我们将某种引用置入orchestra数组中,就会自动向上转型到Insturment。
可以看到,tune()方法完全可以忽略它周围代码所发生的全部变化,依旧正常运行。这正是我们期望多态所具有的特性。我们所做的代码修改,不会对程序中其他不应受到影响的部分产生破坏。换句话说,多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术
本文探讨了面向对象编程中的多态概念,解释了如何通过向上转型实现对象的灵活处理。多态允许将多种类型视为同一类型处理,从而增强了代码的可扩展性和可读性。通过动态绑定,方法调用会根据实际对象类型产生正确的行为,确保程序在添加新类时仍能正确运行。文中以音乐乐器和几何形状为例,展示了多态在实际编程中的应用和优势。
1904

被折叠的 条评论
为什么被折叠?



