在面向对象的程序设计中,多态是继数据抽象和继承之后的第三种基本特性;多态通过分离做什么(基类对象)和怎么做(导出类对象),从另一角度将接口和实现分离开来。多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序—即无论在项目最初创建时还是在需要添加新功能时都可以“生长”的程序。
“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。继承允许将对象视为它自己本身的类型或其基类型(向上转型)来加以处理,多态的作用则是消除类型之间的耦合关系。 多态调用允许一种类型表现出与其他相似类型之间的区别,只要它们都是从同一基类导出来的。这种区别是根据方法行为的不同而表示出来的,虽然这些方法都可以通过同一个基类来调用。
1.1 向上转型
对象即可以作为它自己本身的类型使用,也可以作为它的基类型使用。而这种把对某个对象的引用视为对其基类型的引用的做法
被称为向上转型。但是向上转型时,每个类在调用方法时都会使用父类的对象,而忘记了自己的对象类型。
在上述问题中,我们是否要将会进行向上转型的方法中的参数直接使用成其本身类型的参数呢?这样或许会更加直观。但是那样又会出现一些新的问题:比如说我们要在Instrument(乐器)类型中的每一个子类都编写一个tune()方法,这时当我们以后要在Instrument中添加新的类型时,我们将需要非常多的编程工作,这显然是十分麻烦的。
如果只写这样一个简单方法,它仅接收基类作为参数,而不是那些特殊的导出类。这样情况会变得更好吗?也就是说,如果不管导出类的存在,编写的代码只是与基类打交道,会不会更好?这正是多态所允许的。
多态即是允许一个方法只接收基类作为参数,然后基类的导出类可以通过向上转型方便的将自己的对象当成参数传入该方法中。
1.2 方法调用绑定
说到这里,其实上我们在数据进行向上转型后,对于在方法中如何判断其原本是哪一个对象类型这一问题还没有解决。为了解决这一问题我们将学习方法调用绑定:
将一个方法调用同一个方法主题关联起来被称为绑定。若在程序执行前进行绑定(由编译器和连接程序实现),叫做前期绑定。一个非面向对象编程的编译器产生的函数调用就是使用前期绑定(编译器将产生对一个具体函数名字的调用)。Java中无法使用前期绑定,因为当编译器只有一个Instrument引用时,它无法知道究竟调用哪个方法才对。
而解决这个问题的方法就是后期绑定,它的含义就是在运行时根据对象的类型进行绑定
。后期绑定也叫做动态绑定或运行时绑定。编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。后期绑定根据不同的编程语言有所不同,但是,不管怎样都必须在对象中安置某种 “类型信息”。
Java中所有的方法都是通过动态绑定来实现多态
,我们就可