多态的深层次理解(JAVA)

假设你已经大致了解了多态而对多态的印象有较为模糊,似理解又不能熟练运用,那么就由我来带你梳理一遍。

多态实现的前提

1.要有继承关系

2.子类要重写父类的方法

3.父类引用指向子类对

如果写成程序可以是:

JAVA:
public abstract class Person(){
	private int age = 10;
	private String name = "wyw";
	public void speak(){}
	
}
// 多态条件一:继承
public abstract class Man extends Person(){
	private int age = 40;
	private String name = "hsc";
	private String beard;
	// 多态条件二:重写
	@override
	public void speak(){
		System.out.println("带着胡子讲话")
	}
}
// 多态条件一:继承
public abstract class Woman extends Person(){
	private int age = 20;
	private String name = "kb";
	private String longHair;
	// 多态条件二:重写
	@override
	public void speak(){
		System.out.println("带着长发讲话")
	}
}
// 实例化,
// 多态条件三:父类引用指向子类对(即Person指向多个子类对象)
Person man1 = new Man();
Person Woman1 = new Woman();

这里new的是一个子类对象,却是父类型去接收的,很奇怪吧,那它到底算父类对象还是子类对象呢,其实是子类对象,通俗的说,就是带着爸爸名号的儿子,我的本质是儿子的行为,却披着爸爸的外衣
C++可以用指针来理解,即用一个父类型的指针指向了一个儿子对象。

多态的实现

当程序设定好后,多态的继承具体实现其实是子类对父类的非静态类型的成员方法的重写后使用
即:

man1.age;//打印出来是10
man1.speak();
woman1.speak();

1.子类既可以直接调用父类的成员变量和在子类没有重写过的方法
2.子类也可以调用根据重写父类的非静态方法
3。缺点不能使用子类自己的成员变量和在父类没有定义的方法了

至于上面所讲的可以调用父类的没有被子类重写过的方法还有不能使用子类在父类中没有定义过的方法,这两种情况在现实生活中,很少见到,也不允许发生,其原因。
就要讲到设计模式中的7大原则:

设计模式7大原则与多态的联系

其中里面的里氏替换原则依赖倒置原则,是多态的体现。
开闭原则则是封装的体现。我们具体来讲讲里氏和依赖原则。

里氏替换原则:

里氏替换的数学定义很复杂,简单地说是:一个软件实体如果使用了是一个父类,那么把子类换成父类继续使用,软件不会出现一点错误。这是观察到的现象。
其实反过来说就是:若是满足了里氏替换原则:我们写程序时子类必须能够替换掉他们的父类,即子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。也就是说,我们设计父类时,必须让父类足够抽象,能够让子类实现父类的所有方法成为可能
就比如说我们有鸵鸟麻雀,此时我们设计鸟类为父类就不太好,因为鸟类有个行为是飞行,而子类鸵鸟并不会飞,它就无法作为子类去全部实现父类的行为,此时我们在抽象一点设为动物类为父类叫稳妥。
正是有了里氏替换继承的开闭原则才成为了可能性。
尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。

因此,多态中的父类引用的子类对象可以调用父类在子类中没有重写的方法,这种情况在实际工程中不允许发生,子类必须实现父类的所有方法,不然扩展性就会变差,父类不是真正的被复用。

子类完全继承了父类的方法**,但是又多出了父类所没有定义的方法,也是可以的,工程中也有很多这样的例子,也有可能是因为这个父类没有设计好。**

JAVA:
public abstract class Birds(){
	private int age = 10;
	private String name = "wyw";
	public void speak(){}
	public void fly(){}
	
}
// 多态条件一:继承
public abstract class Ostrich extends Birds(){
	private int age = 40;
	private String name = "hsc";
	// 多态条件二:重写
	@override
	public void speak(){
		System.out.println("鸵鸟讲话")
	}
}
// 多态条件一:继承
public abstract class Sparrows extends Birds(){
	private int age = 20;
	private String name = "kb";
	// 多态条件二:重写
	@override
	public void speak(){
		System.out.println("麻雀讲话")
	}
	
	@override
	public void fly(){
		System.out.println("麻雀飞行")
	}
}
// 实例化,
// 多态条件三:父类引用指向子类对(即Birds指向多个子类对象)
Birds ostrich1 = new Ostrich();
Birds sparrows1 = new Sparrows();
ostrich1.fly();// 由于子类未重写父类的方法。fly向上转型去寻找父类方法
sparrow1.fly();// 麻雀子类重写了父类方法,fly向下转型去调用子类重写的方法

需注意:

1.上述程序中的子类未完全实现父类的方法是一定要尽量避免的情况
2.子类如果不饿能完整实现父类方法,则应断开父子关系,采用依赖,聚合组合关系代替继承

依赖倒置原则:

即程序中的所有依赖关系都是应该终止于抽象类或者接口,这才是面向对象的标志。
其编程时的核心思想为:高层模块不应该依赖底层模块,二者都应该依赖于其抽象,抽象不应该依赖细节具体实现,而具体细节应该依赖抽象。
再通俗的讲就是编程时,通过抽象类或接口让各个类的实现独立,实现松耦合,让修改难度下降。
例如现在有三个模块1.人模块 2.慢步模块 3.猩猩模块4.快跑模块,假设人和猩猩都是作为高层模块负责行为逻辑,而慢步和快跑模块是作为底层模块负责具体实现。
现在我人和猩猩模块调用了慢步模块的方法,那么就会使高层模块依赖底层模块,那么当要修改这个我人慢跑的速度时,随之而来的改变是人模块方法变动,慢跑模块的具体实现的方法变动,导致多处改动。
正确做法:我将人和猩猩的都需要的慢跑和快跑抽象为接口形式,具体实现任由慢快两个模块实现,而我人和猩猩只需要调用接口即可实现解除了高层依赖底层模块,让他们都依赖抽象接口,实现松耦合,修改时也只需要修改一处模块即可。

扫描二维码关注公众号,回复: 8887745 查看本文章

所以有些操作工程上不允许发生,那么我个人感觉多态唯一的缺点可能只是不能调用子类的成员变量了。

设计模式7大原则具体(附):

在运用面向对象的思想进行软件设计时,需要遵循的原则一共有7个,他们是:

1. 单一职责原则(Single Responsibility Principle)

每一个类应该专注于做一件事情。

2. 里氏替换原则(Liskov Substitution Principle)

超类存在的地方,子类是可以替换的。

3. 依赖倒置原则(Dependence Inversion Principle)

实现尽量依赖抽象,不依赖具体实现。

4. 接口隔离原则(Interface Segregation Principle)

应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。

5. 迪米特法则(Law Of Demeter)

又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。

6. 开闭原则(Open Close Principle)

面向扩展开放,面向修改关闭。

7. 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)

尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。

发布了19 篇原创文章 · 获赞 4 · 访问量 488

猜你喜欢

转载自blog.csdn.net/qq_35050438/article/details/104072868