【JAVA】 多态-上溯造型

上溯造型

之所以叫作上溯造型,除了有一定的历史原因外,也是由于在传统意义上,类继承图的画法是根位于最顶部,再逐渐向下扩展
由于造型的方向是从衍生类到基础类,箭头朝上,所以通常把它叫作“上溯造型”,即Upcasting。上溯造型肯定是安全的,因为我们是从一个更特殊的类型到一个更常规的类型。换言之,衍生类是基础类的一个超集。它可以包含比基础类更多的方法,但它至少包含了基础类的方法。进行上溯造型的时候,类接口可能出现的唯一一个问题是它可能丢失方法,而不是赢得这些方法。这便是在没有任何明确的造型或者其他特殊标注的情况下,编译器为什么允许上溯造型的原因所在。

//: Wind.java
// Inheritance & upcasting
import java.util.*;

class Instrument {
  public void play() {}
  static void tune(Instrument i) {
    // ...
    i.play();
  }
}

// Wind objects are instruments
// because they have the same interface:
class Wind extends Instrument {
  public static void main(String[] args) {
    Wind flute = new Wind();
    Instrument.tune(flute); // Upcasting
  }
} ///:~

这个例子中最有趣的无疑是tune()方法,它能接受一个Instrument句柄。但在Wind.main()中,tune()方法是通过为其赋予一个Wind句柄来调用的。由于Java对类型检查特别严格,所以大家可能会感到很奇怪,为什么接收一种类型的方法也能接收另一种类型呢?但是,我们一定要认识到一个Wind对象也是一个Instrument对象。而且对于不在Wind中的一个Instrument(乐器),没有方法可以由tune()调用。在tune()中,代码适用于Instrument以及从Instrument衍生出来的任何东西。在这里,我们将从一个Wind句柄转换成一个Instrument句柄的行为叫作“上溯造型”。

取得一个对象句柄,并将其作为基础类型句柄使用的行为就叫作“上溯造型”。

为什么要上溯造型?
假如只写一个方法,不使用特定的衍生类,而是将基础类作为自变量或参数使用,会简单很多。也就是说,如果能不顾衍生类,只让代码与基础类打交道,那么省下的工作量将是难以估计的。

方法调用绑定(Method-call binding)

将方法调用同方法主体连接起来称为“绑定”(Binding)。
若在程序运行之前执行绑定(由编译器和链接程序执行,如果有的话),则称作“早期绑定”(early binding)。
当程序只有基础类的句柄时,编译器不知道调用哪个方法是正确的。后期绑定(late binding)能够解决这个问题。后期绑定也叫作“动态绑定”(dynamic binding)或“运行期绑定”(runtime binding),指的是根据对象的类型在运行期间(runtime)进行绑定

实现了后期绑定的语言,必须提供一种机制,即在运行期间判断对象的类型,然后调用相应的方法。也就是说,编译器此时还是不知道对象的类型,但方法调用机制(method-call binding)会去找到并调用正确的方法主体。不同的语言对后期绑定的实现方法各有区别。但是可以设想,它们都必须在对象中安插某些特殊类型的信息。

Java中绑定的所有方法都采用后期绑定,除非一个方法已被声明成static或final(private方法是隐式的final)。开发者不必决定是否应进行后期绑定——后期绑定是自动发生的。

上溯造型的具体实现

Java里所有绑定的方法通过后期绑定具有多态性后,就可以相应地编写自己的代码,并使其和基础类沟通。

在此以Shape举例:
Shape为基类;Circle(圆形),Square(方形),Triangle(三角形)等等是衍生类:
在这里插入图片描述
上塑造型语句:
Shape s = new Circle();
我们创建了Circle对象,并将结果句柄立即赋给一个Shape。这表面看起来似乎属于错误操作(将一种类型分配给另一个),但实际是完全可行的——因为按照继承关系,Circle属于Shape的一种。因此编译器认可上述语句,不会向我们提示一条出错消息。
当我们调用其中一个基础类方法时(已在衍生类里覆盖):
s.draw();
调用已在Circle中重写过的draw()方法,注意Shape中也有同名的draw()方法
s.draw() 调用的是Circle中的draw()方法

//: Shapes.java
// Polymorphism in Java

class Shape { 
  void draw() {}
  void erase() {} 
}

class Circle extends Shape {
  void draw() { 
    System.out.println("Circle.draw()"); 
  }
  void erase() { 
    System.out.println("Circle.erase()"); 
  }
}

class Square extends Shape {
  void draw() { 
    System.out.println("Square.draw()"); 
  }
  void erase() { 
    System.out.println("Square.erase()"); 
  }
}

class Triangle extends Shape {
  void draw() { 
    System.out.println("Triangle.draw()"); 
  }
  void erase() { 
    System.out.println("Triangle.erase()");
  }
}

public class Shapes {
  public static Shape randShape() {
    switch((int)(Math.random() * 3)) {
      default: // To quiet the compiler
      case 0: return new Circle();
      case 1: return new Square();
      case 2: return new Triangle();
    }
  }
  public static void main(String[] args) {
    Shape[] s = new Shape[9];
    // Fill up the array with shapes:
    for(int i = 0; i < s.length; i++)
      s[i] = randShape();
    // Make polymorphic method calls:
    for(int i = 0; i < s.length; i++)
      s[i].draw();
  }
} ///:~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值