你的位置:首页 > Java教程

[Java教程]从stackoverflow上的一个问题看Java动态绑定


我们先来看stackoverflow上的一个问题,先上代码

 1 public class Piece{ 2   public static void main (String [] args){ 3     Piece p2 = new Knight();  4     Knight p1 = new Knight(); 5     p1.capture(p2);   6     p2.capture(p1);   7   } 8   public void capture(){ 9     System.out.println("Capture");10   }11   public void capture(Piece p){12     System.out.println("I'm bored");13   }14 }15 class Knight extends Piece{16   public void capture(Piece p){    17     System.out.println("Knight is bored");18   }19   public void capture(Knight k){   20     System.out.println("Too slow buddy");21   }22 }

问的是这段代码输出是什么?答案是

Knight is boredKnight is bored

因为刚刚开始学习Java,在跟CS61B的课,然后老师讲到dynamic type和static type的时候,自己有点模糊,于是在stackoverflow上搜索相关的问题,果然发现一个典型的动态绑定的问题,仔细分析之后感觉收获挺大的,对继承的理解更加深入了。

分析:这个是怎么回事呢?首先看第三第四行,p1肯定是一个Knight类型,而对于p2, 它是一个Piece类型的引用,指向了一个Knight类的对象,此时我们称Piece为static type,而Knight为dynamic类型。

p1.capture(p2),也就是Knight.capture(Piece),Knight类里刚好定义了这个方法,即输出"Knight is bored"。

p2.capture(p1),也就是Piece.capture(Knight),乍一看Piece里面没有这个方法啊。这里应该是有一个赋值,即Piece p = p1,把子类的引用赋给父类,这是允许的。所以实际上调用的Piece.capture(Piece),那么结果应该输出"I'm bored"啊,怎么会是"Knight is bored"呢?前面已经说了,p2实际上指向了一个Knight类的对象,而在Knight类中刚好Ovriride了这个Piece.capture(Piece)方法,也就是Knight.capture(Piece),所以应该调用这个方法,即输出"Knight is bored"。

 

好现在我们把这个Override的方法注释掉,看看结果如何?

 1 public class Piece{ 2   public static void main (String [] args){ 3     Piece p2 = new Knight(); //static Piece, dynamic Knight 4     Knight p1 = new Knight(); 5     p1.capture(p2);   6     p2.capture(p1);   7   } 8   public void capture(){ 9     System.out.println("Capture");10   }11   public void capture(Piece p){12     System.out.println("I'm bored");13   }14 }15 class Knight extends Piece{16   // public void capture(Piece p){    //这里override了Piece中的方法17   //   System.out.println("Knight is bored");18   // }19   public void capture(Knight k){   20     System.out.println("Too slow buddy");21   }22 }

输出结果是:

I'm boredI'm bored

分析:第二个输出刚刚已经解释了,因为现在没有override的方法了,所以就直接调用Piece类中的Piece.capture(Piece p)方法。那么第一个输出又是怎么回事呢?p1.capture(p2),也就是Knight.capture(Piece),这里不能调用Knight.capture(Knight k)这个方法,因为Knight k = p2,这是不合法的,Java中不允许把父类的引用赋给子类的引用变量。那么既然如此,Knight类中岂不是没有这个方法了吗?不要忘记,Knight类是继承在Piece类上的,也就是Knight类型也能调用Piece类中的方法,也就是Piece.capture(Piece)方法,所以输出是"I'm bored"。

 

现在我们知道,Piece p2 = new Knight()这句特殊之处就在于,有一个查找在子类中是否存在override的方法,如果存在就调用子类中这个override的方法,如果没有就使用父类中的方法。如果是这样定义的:Piece p2 = new Piece(),那么无论在子类中有没有override方法,都不会去调用子类的方法,而是直接调用父类的方法。如下:

 1 public class Piece{ 2   public static void main (String [] args){ 3     // Piece p2 = new Knight(); //static Piece, dynamic Knight 4     Piece p2 = new Piece(); 5     Knight p1 = new Knight(); 6     p1.capture(p2);   7     p2.capture(p1);   8   } 9   public void capture(){10     System.out.println("Capture");11   }12   public void capture(Piece p){13     System.out.println("I'm bored");14   }15   public void capture(Knight p){16     System.out.println("Hello");17   }18 }19 class Knight extends Piece{20   public void capture(Piece p){    //这里override了Piece中的方法21     System.out.println("Knight is bored");22   }23   public void capture(Knight k){   //这里override了Piece中的方法24     System.out.println("Too slow buddy");25   }26 }

输出结果是:

Knight is boredHello

我们可以看到,即使在Knight类中override了.capture(Knight)方法,由于p2一直就是一个Piece类,根本不会调用Knight类中的方法,所以p2.capture(p1),也就是Piece.capture(Knight),输出"Hello"。第四行改成Piece p2 = new Knight(),则输出就是:

Knight is boredToo slow buddy

第二个输出调用的就是Knight类中override的方法。