代码改变世界

我们为什么需要override关键字

2006-07-03 15:57 FantasySoft 阅读(...) 评论(...) 编辑 收藏

        一直以来,我都觉得C#的继承体系结构太过繁琐,既要声明方法是否为virtual,又要使用new、override这样的关键字界定派生类方法在继承体系中的角色,远不如Java的继承实现来得简洁清爽。在Java当中,所有的类方法在默认情况下都是virtual的,所以就省下了将方法声明为virtual这个步骤。也许你会问,如果想声明一个非virtual的方法怎么办呢?所谓非virtual就是不允许继承了,那么在方法的签名中使用final关键字即可。从继承实现的最基本的虚方法声明中,我们可以看出两种语言在设计上不同的关注点:Java会认为基类方法在大部分情况会被覆写(override),而C#则相反。

        尽管C#的继承体系结构显得有些臃肿,但是,我也坚信存在即是真理,C#采用这样的方式实现继承也必然出于其它方面的考虑,至少当我知道C#是这样实现继承的时候,“严谨”一词就在我的脑海中不断闪现。不过,光是“严谨”这种感性的认识还不足以让我对C#的继承实现感到心悦诚服,毕竟作为程序员的我喜欢纯粹,也喜欢简约。于是乎,对C#继承体系结构的偏见就根深蒂固地扎根于我的认知土壤中了。直到最近在开发中碰到了一个问题,解决的过程让我明白到严谨是很重要的,不仅是程序员的态度,还有编程语言本身的支持。
      
        遇到的问题其实很简单,就是派生子类覆写了父类的一个方法,而该父类方法的参数发生了改变,编译器在编译过程中并没有发现这个问题。在程序运行的时候,则出现了死循环,并且没有任何的错误提示。这个bug着实让人很迷惑,也耗费了同事很多的时间,最后才发现是由于父类方法发生了改变,而子类中的覆写方法又没有更新造成的。请参见以下简化的代码:

public class ParentClass {
    
public int test(int i) {          
        
return i * 2;
    }

}


public class DerivedClass extends ParentClass {
    
public int test(int i) {
        
return i * 2 + 1;
    }

}


public class Test {
    
public static void main(String[] args) {
        ParentClass a 
= new DerivedClass();
        System.out.println(a.test(
0));    //输出结果为1
    }

}


接着,ParentClass的代码改为:

public class ParentClass {
    
public int test(int i, boolean flag) {          
        
if (flag) 
            
return i * 2;
        
else
            
return 0
    }

}


通过IDE的支持,我们可以找到使用test方法的地方,也就将main函数中的调用改为:System.out.println(a.test(0, true)); 虽然这样的改动并不会引发编译错误,在运行的时候也没有问题,但是很明显,输出结果变为了0,程序已经不再按照我们的意图而执行了,因为调用的是从父类继承过来的test方法,而不是我们原来所要求的调用覆写的方法了。像这样的bug是很难被发现的,如果不是因为程序在运行过程中出现了死循环,都不知道这个潜在的bug会在什么时候突然爆发。

        以上给出的例子已经很好的说明了C#使用override关键字的必要性。对应着C#,以上代码的DerivedClass中的test方法就要被声明为override,一旦ParentClass中的test方法发生了改变,编译器就会在编译过程发现这个错误。可见语言定义的严谨还是能够在很大程度上帮助程序员在开发中少犯错的。事实上,Java也意识到了这个方面的不足,也在语言定义上作了改进:通过使用内建的Override注释(Annotation)来避免以上提到的问题(参见参考文章[1])。个人觉得,Java的解决方案似乎更加优美,因为它是通过提供一个机制实现了继承上的制约,而非增加关键字。


         参考文章:[1] 在Eclipse 3.1中体验J2SE 5.0的新特性 : 第二部分 :注释类型 
                         [2] 了解何时使用 Override 和 New 关键字(C# 编程指南)  
                         [3] 使用 Override 和 New 关键字进行版本控制(C# 编程指南)