多态 二

(三)协变返回类型

JavaSE5中添加了协变返回类型:在导出类中被覆盖的方法可以返回基类方法的返回类型的某种导出类型。
如下:

class Grain {
	public String toString() {
		return "Grain";
	}
}

class Wheat extends Grain {
	public String toString() {
		return "Wheat";
	}
}

class Mill {
	Grain process() {
		return new Grain();
	}
}

class WheatMill extends Mill {
	Wheat process() { // 协变。允许导出类的返回值的具体类型,不必强制覆盖。
		return new Wheat();
	}
}

public class CovariantReturn {
	public static void main(String[] args) {
		Mill m = new Mill();
		Grain g = m.process();
		System.out.println(g);
		m = new WheatMill();
		g = m.process();
		System.out.println(g);
	}
}

结果为:

Grain
Wheat

(四)用继承进行设计

注意:

  • 更好的方式是首先选择组合。更加灵活,可以动态的选择类型(也就是选择了行为),继承在编译时期就要知道确切的类型。
  • 一条通用准则:“用继承表示行为间的差异,并用字段表达状态上的变化”

如下:

class Actor {
	public void act() {
	}
}

class HappyActor extends Actor {
	public void act() { // 继承:表达act()方法的差异
		System.out.println("HappyActor");
	}
}

class SadActor extends Actor {
	public void act() {
		System.out.println("SadActor");
	}
}

class Stage {// 组合:使状态发生改变->行为的改变
	private Actor actor = new HappyActor();// 可以在运行时与另一个不同的对象绑定起来
//获得了动态灵活性(也被称为状态模式)
	public void change() {
		actor = new SadActor();
	}

	public void performance() {
		actor.act();
	}
}

public class Transmogrify {
	public static void main(String[] args) {
		Stage stage = new Stage();
		stage.performance();
		stage.change();
		stage.performance();
	}
}

结果为:

HappyActor
SadActor

1. 纯继承与扩展

注意:

  • is-a的关系。类的接口已经确定了它应该是什么,集成可以确保所有的导出类具有基类的接口,可以认为是一种“纯替代”。
  • 缺点:导出类中扩展的部分不能被基类访问,即:一旦上转型,则不能调用新方法。
  • 通常情况下,我们需要重新查明对象的确切类型,以便能够发访问类型所扩展的方法。

2. 向下转型与运行时类型的识别

注意:

  • 基类不会具有大于导出类的接口。
  • 在Java语言中,所有的转型都会得到检查。即使只是一次加括弧形式的类型转型,在运行期仍会检查。
  • 在运行期间进行检查的行为称作:运行时类型识别(RTTI)。
    如下:
class Useful {
	public void f() {
	}

	public void g() {
	}
}

class MoreUseful extends Useful {
	public void f() {
	}

	public void g() {
	}

	public void u() {
	}

	public void v() {
	}

	public void w() {
	}
}

public class RTTI {
	public static void main(String[] args) {
		Useful[] x = { new Useful(), new MoreUseful() };
		x[0].f();
		x[1].g();
		// 编译时,在Useful中没有被发现
		// x[1].u();
		((MoreUseful) x[1]).u();
		// ((MoreUseful) x[0]).u();

	}

}

(七)总结

  • 如果不运用数据抽象和继承,就不可能理解甚至创建多态的例子。
  • 多态是一种不能单独来看待的特性,需要与其他特性协同工作。
  • 为了有效的使用多态乃至面向对象技术,需要扩展编程视野,使其不仅包括个别类的成员和消息,而且还要包括类与类之间的共同特性以及它们之间的关系。
  • 尽管需要极大的努力,这样做是值得的。因为它可以带来很多成效,更快的程序开发过程,更好的代码组织,更好的扩展的程序以及更容易的代码维护。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
多态是面向对象编程的一个重要特性,它允许在派生类中重写基类中的同名方法,并且在运行时根据对象的实际类型来选择正确的方法实现。多态性可以通过虚函数来实现。 在多态性的实现中,程序可以使用基类指针或引用指向派生类对象,并调用同名的虚函数。在运行时,程序会根据指针或引用指向的对象的实际类型来选择正确的函数实现。 例如,我们可以定义一个基类 `Shape` 和它的两个派生类 `Circle` 和 `Rectangle`,并在 `Shape` 类中声明一个虚函数 `draw()`,在派生类中重写 `draw()` 方法: ```c++ class Shape { public: virtual void draw() { // 基类中的默认实现 } }; class Circle : public Shape { public: void draw() override { // 画圆形的实现 } }; class Rectangle : public Shape { public: void draw() override { // 画矩形的实现 } }; ``` 现在,我们可以使用指向 `Shape` 类的指针或引用来指向 `Circle` 或 `Rectangle` 对象,并调用它们的 `draw()` 方法: ```c++ Shape* s1 = new Circle(); Shape* s2 = new Rectangle(); s1->draw(); // 调用 Circle 类中的 draw() 方法 s2->draw(); // 调用 Rectangle 类中的 draw() 方法 ``` 在这个例子中,`s1` 指向一个 `Circle` 对象,因此调用 `s1->draw()` 时会调用 `Circle` 类中的 `draw()` 方法。类似地,`s2` 指向一个 `Rectangle` 对象,因此调用 `s2->draw()` 时会调用 `Rectangle` 类中的 `draw()` 方法。 多态性让程序具有更高的灵活性和可扩展性,因为它允许我们在不改变基类代码的情况下增加新的派生类。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值