`

[#0x000C] 有关向下转型的必要性和动态绑定的细节

    博客分类:
  • Java
阅读更多

  在[#0x0009]里面说过,“除了static方法和final方法(final包含private)外,Java对其他所有的方法都采用dynamic binding”(P151, Chapter 8, Thinking in Java, Fourth Edition),不过下面的这个例子也许会让人有点吃惊(adapted from Chapter 8, Thinking in Java, Fourth Edition):

//@file RTTI.java
//RTTI: Run-Time Type Identification

class Useful 
{
	public void f() {System.out.println("Useful.f()");}
}

class MoreUseful extends Useful 
{
	public void f() {System.out.println("MoreUseful.f()");}
	public void g() {System.out.println("MoreUseful.g()");}
}	

public class RTTI 
{
	public static void main(String[] args) 
	{
		Useful x = new MoreUseful();
		
		x.f();
		//x.g();//编译错误:找不到符号
		((MoreUseful)x).g(); // Downcast/RTTI
	}
}
//output:
/*
	MoreUseful.f()
	MoreUseful.g()
*/

  按理来说,x.f()通过动态绑定能够正确调用MoreUseful的f()方法,那么为什么x.g()就不行呢?真的是“除了static方法和final方法(final包含private)外,Java对其他‘所有’的方法都采用dynamic binding”吗?还是只对覆写方法才动态绑定?

  其实这里涉及到动态绑定的细节问题。当然,的确是“除了static方法和final方法(final包含private)外,Java对其他所有的方法都采用dynamic binding”,只不过在使用动态绑定之前,编译器还做了一些其他的工作,而这些工作,就是造成上面代码结果的原因。

  Java的方法调用过程:

  ->编译器查看引用(x)的声明类型(Useful)和方法名(g());

    -->通过声明类型找到方法列表;

      ---->如果方法名不在方法列表中,则编译器报错(g()不在Useful的方法列表里,所以出错);

      ---->如果方法名在方法列表中,则继续下列步骤;

  ->编译器查看方法的参数列表,获取参数方法签名;

    -->如果方法是private、static、final或者构造器,编译器就可以确定调用那个方法(这是静态绑定);

    -->如果不是上述情况,就要使用动态绑定;

  可见,x.g()出错是由于使用动态绑定前的方法名检查未通过。从这个角度来说,动态绑定似乎的确只适用于覆写方法。

  由于无法使用动态绑定,所以要正确调用x.g()方法,向下转型((MoreUseful)x)就必不可少了。这里x必须指向一个实际的MoreUseful对象(即是通过向上转型得来),如果Useful x = new Useful(),那么((MoreUseful)x)会编译报错。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics