将一个方法调用同一个方法主体(大多时候为派生类)关联起来被称做绑定。在程序执行前进行绑定,由编译器和链接程序实现称为前期绑定。
后期绑定:也称动态绑定(在程序运行过程中动态实现绑定)如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用适当的方法。 也就是说,编译器不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。后期绑定机制随编程语言的不同而有所不同。
JAVA中除了static 方法和final方法之外,其他所有的方法都是后期绑定。(也就是说,后期绑定会自动发生)
EX:
//shape/Shape.java
package polymorehism.shape
import static net.arborj.util.Print.* //简化print的一个工程类
public class Shape{
public void draw(){
print("Shape draw");
}
}
//:shape/ Circle.java
package polymorehism.shape
public class Circle externs Shape{
public void draw(){
print("Circle drwa");
}
}
//shape/ Square.java
package polymorehism.shape
public class Square externs Shape{
public void draw()
{
print("Square draw");
}
}
public class Main{
public static void main(String[] args){
Shape s = new Circle;
s.draw();
}
}
//输出(子类成员): Circle draw
多态也不适用于属性。
这里虽然是一个Shape引用,后期绑定(多态)中调用了Circle.draw()方法。 注意Shape类中的draw方法不能是static ,(private或者final)的 ,static方法与类本身关联,与对象无关,而private方法属于finnal方法,派生类(也叫导出类) 不能访问,实现的重名draw()是一个全新的函数(与基类无关) ,故如果通过基类引用调用,只会调用基类方法(不能按照我们所期望的来运行。
还有基类中的属性也不适用于多态。
--------------------JAVA中多态与工厂(factory)
生成工厂类Generator:
public class RandomShapeGenerator{
private Random rand = new rand(47);
public Shape next(){
switch(rand.next(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(Strings[] args){
Shape[] s= new Shape[9];
for(int i=0;i<9;i++)
{
s[i]=gen.next();
for(Shape shp:s)
{
shp.draw();
}
}
}
}
Shape基类为自它继承而来的所有导出类建立了一个公共接口。 RandomShapeGenerator是一个工厂类,它在每次随机选择的Shape对象产生一个引用(我们可以通过对工厂类传递参数从而通过switch返回我们需要的子类引用)。 因为程序只与基类接口通信,这样的程序可扩展的,
可以从通用的基类继承出新的数据类型,从而添加一些新功能。(为基类添加新函数)而那些操纵基类接口的方法不需要任何改动就可以应用于新类。(将改变的事务与未变的食物分离开的)
-----------------------------------------C++中的多态。
先来看跟JAVA相同例子:
class Shape{
public:
void draw();
}
void Shape::draw(){
pirntf("Shape draw");
}
class Circle:public Shape{
public:
void draw();
}
Circle::draw(){
printf("Circle draw");
}
class Square:public Shape{
public:
void draw();
}
Circle::draw(){
prinf("Square draw ");
}
int main()
{
Circle sc1;
Square sc2;
Shape *s[] = {&sc1,&sc2};
for(int i=0;i<2;i++)
{
s[i]->draw();
}
}
//输出基类成员Shape draw Shape draw
即通过基类指针调用的都是基类中定义的函数,派生类中的函数不会被调用
C++中的多态依赖于虚函数
虚函数必须是非静态的成员函数。
虚函数的声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。
赋值兼容规则:在需要基类对象的任何地方都可以使用公有派生类的对象。
1:派生类对象可以赋值给基类对象。
2:派生类对戏那个可以初始化基类的引用。
3:派生类对象地址可以赋值给指向基类的指针。
C++运行时多态满足三个条件:1,类之间满足赋值兼容规则。2基类中虚函数用virtual声明。
3:由成员函数来调用或者通过指针,引用来访问虚函数。
改进:
class Shape{
public:
virtual void draw();
}
void Shape::draw(){
pirntf("Shape draw");
}
class Circle:public Shape{
public:
void draw(); //覆盖基类的虚函数
}
Circle::draw(){
printf("Circle");
}
class Square:public Shape{
public:
void draw(); //覆盖基类的虚函数
}
Circle::draw(){
prinf("Square");
//Shape::draw(); 可以通过这种方式调用基类被覆盖的函数。
}
int main()
{
Circle sc1;
Square sc2;
Shape *s[] = {&sc1,&sc2};
for(int i=0;i<2;i++)
{
s[i]->draw();
}
}
//输出子类成员:Circle draw , Square draw .
必须要通过指针或者引用来访问虚函数,如果是通过对象名来访问虚函数,则绑定在编译过程中就可以进行(静态绑定),而无需在运行过程进行。(