论坛首页 Java版 OO

override, overload, covariance

浏览 6215 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2006-06-21
java语法如此多娇,引无数英雄竞折腰。

java语法细抠起来,极其诡异,充满了陷阱。
尤其是java语法里面的继承问题。
论坛里面关于java语法里面的继承问题的讨论,
可以说是长篇累牍,罄竹难书。
长江后浪推前浪,江上代有才人出。
前仆后继,香火不绝。

我只是希望下面的内容,
a. 一劳永逸地阐述清楚关于java继承的相关问题。
能够帮助一些年轻朋友节省宝贵的时间. 并且
b. 不必把java语法当作OO语法宝典来理解。java语法也在不断地变化。其变化理由也非常理可以度之。

1. override vs overload
首先来看诡异度最低的 override, overload.

(1) override
class Parent{
Object func(Number n){
...
}
}

class Child extends Parent{
Object func(Number n){
...
}
}

这叫做override.
这时候,真是痛感c#里面引入override关键字的必要性。
java里面也真应该引入这个关键字。免得含糊不清,误导使用者。

override 的含义,就是俗称的多态。
Child.f 和 Parent.f 可以看作是占用了虚函数指针表里面的同一个Entry。
所以,实际上相当于Child.f 覆盖了Parent.f。
所以,override有时候翻译为 覆盖。

Parent vtable
Entry 1 : Parent.f

Child vtable
Entry 1 : Child.f


override 是 运行时动态决定调用哪个method. 所以叫做多态性。

(2) overload

class Child{
Object func(Number n){
...
}

Object func(String s){
...
}
}

这叫做overload。两个func完全是不同的函数。只是恰好函数名相同。这两个func在虚函数指针表占用了两个不同的Entry.
vtable
Entry 1: void func(Number)
Entry 2: void func(String)

一定要记住:
overload 是 编译期就决定了调用哪个函数。
编译器根据参数类型的不同,产生了不同的调用函数地址(不同的Entry)的jvm指令。

--

overload为啥翻译为重载。这可难住我了。重载在中文里面到底什么意思。我确实不清楚。我强烈怀疑重载完全是一个生造词。类似于 阿尔卑斯白 这类词汇。只可意会,不可言传。

--

class Parent{
Object func(Number n){
...
}
}

class Child extends Parent{
Object func(String s){
...
}
}

这还是叫做overload.
parent vtable
Entry 1: void func(Number) of parent

child vtable
Entry 1: void func(Number) of parent
Entry 2: void func(String) of child

--

class Parent{
Object func(Number n){
...
}
}

class Child extends Parent{
Object func(Number n){
...
}

Object func(String s){
...
}
}

这里面同时有override, 和 overload
parent vtable
Entry 1: void func(Number) of parent

child vtable
Entry 1: void func(Number) of child
Entry 2: void func(String) of child

--
强烈建议避免overload,
强烈建议不要自找麻烦
强烈建议采用不同的method name,写成

Object funcNumber(Number n){
...
}

Object funcString(String s){
...
}

2.
下面看诡异度较高的covariance.

Covariance means that the type of arguments, return values, or exceptions of overriding methods can be subtypes of the original types.
Java
Exception covariance has been supported since the introduction of the language. Return type covariance is implemented in the Java programming language version J2SE 5.0. Parameter types have to be exactly the same (invariant) for method overriding, otherwise the method is overloaded with a parallel definition instead.

上面的能理解就理解。不能理解看下面的例子,然后再回头理解。

(1) override -- covariance of return value and/or exception
class Parent{
Object func(Number n) throws Exception{
...
}
}

class Child extends Parent{
String func(Number n) throws SQLException {
...
}
}

这叫做override。因为child func method的返回值和Exception都parent funct method的返回值和Exception的子类。
SQLException extends Exception (since first version)
String extends Object, (since J2se 5.0)

所以,这是overrider.
parent vtable
Entry 1: Object func(Number n) throws Exception of Parent

child vtable
Entry 1: String func(Number n) throws SQLException of Child

(2)overload - no support covariance of parameter type
class Parent{
Object func(Number n){
...
}
}

class Child extends Parent{
Object func(Integer i) {
...
}
}

这是overload。因为java不支持method参数类型的covariance。

parent vtable
Entry 1: Object func(Number n) of Parent

child vtable
Entry 1: Object func(Number n) of Parent
Entry 2: Object func(Integer i) of Child

在这种极其变态诡异的overload情况下,年轻朋友们最着迷的游戏就是,你猜,你猜,你猜猜猜。

Number n = new Integer(0);
Integer i = new Integer(1);
Number nNull = null;
Integer iNull = null;

Child child = new Child();

child.func(n);
child.func(i);
child.func(nNull);
child.func(iNull);

child.func(null);

你猜,这几次都是调用哪个方法啊? 其乐无穷。

这个不用我猜。是编译器需要猜。
编译器根据参数类型进行猜测。猜得到,它就猜。猜不到,它就让你编译不过。
上面的,编译器能猜得到。
编译器才不管你运行的时候,真正的类型是什么。
它只按字面意思理解。你定义的参数,对应的变量是什么类型。它就选什么类型。如果两个类型都匹配,那么就猜更具体的类型。
如果都猜不到,那么就让你编译不过,说你是 含糊不清,二义性,ambiguous
编译器冷笑着:小样儿,跟我玩,你还嫩。

这种游戏,我不玩。
喜欢玩的朋友可以自己验证,根据下面的Proof里面给出,javap 得出jvm指令的方法。


3. Proof

最直接的验证方法,就是查看编译后的JVM指令。

javap -verbose yourClassName > someFile.txt

-------------------------

4. member variable visibility scope in the inheritance
like private, protected, public, package,...

Just data.
Nothing to say.

I refuse to give any comment on the notorious "member variable visibility scope in the inheritance" issue.
   
时间:2006-06-21
赞一个,一直觉得自己对override和overload理解还是比较不错的,今天又发现有了covariance这个说法。呵呵,这个应该是为了描述override和overload之间比较混淆的方式而出现的吧?

C#里面约束的比较好,要么就override,要么就new,要么就overload。开始还认为这种做法太细了,有点小家子气,弄的我很是不爽。今天看了这篇文章,觉得这样还是有好处的。:)
   
0 请登录后投票
时间:2006-06-21
LuckyFox 写道
赞一个,一直觉得自己对override和overload理解还是比较不错的,今天又发现有了covariance这个说法。呵呵,这个应该是为了描述override和overload之间比较混淆的方式而出现的吧?

C#里面约束的比较好,要么就override,要么就new,要么就overload。开始还认为这种做法太细了,有点小家子气,弄的我很是不爽。今天看了这篇文章,觉得这样还是有好处的。:)


呵呵,covariance的意思简单,说起来还真费劲。我就在上面copy了一段英文。后面举了几个例子。大意就是说。

Parent 和 Child 都有 func method.

Java 语法支持 method Exception covariance.
如果 child func method 的Exception 继承了 parent func method 的Exception。比如,SQLException extends Exception,
所以,child func method OVERRIDE parent func method.


Java 语法支持 method Return value covariance. (since j2se5.0)
如果 child func method 的返回值类型,继承了 parent func method 的返回值类型。比如,String extends Object,
所以,child func method OVERRIDE parent func method.

Java 语法不支持 method parameter covariance
如果 child func method 的参数类型,继承了 parent func method 的参数类型。比如,Integer extends Number, 这也白费。
child func method NOT override parent func method

既然不能override,那就只能多出来一个不同的新method。这就是overload.
我只是为了说明,method parameter sub type 的这种overload 尤其诡异,大部分的"你猜猜猜"游戏都是这种类型。

covariance是类型系统里面的重要基础概念,和override, overload没有直接关系。
或者可以说,和override有关系。只要语法支持covariance,程序写法上存在类型继承(sub type)关系,就是override.


如果说在上述例子中有什么关系。那就是

由于java 语法不支持 parameter covariance。
所以,parameter 类型是否存在继承关系都毫无用处,都只能是 overload, 而不能是 override。
   
0 请登录后投票
时间:2006-06-22
我认为JAVA还不错,和C++类似,不像DELPHI和C#真是讨厌;
用过DELPHI的都知道有这么个关键字:reintroduce
而用过C#的应该知道有这么个关键字:new(用在声明方法中)
他们意思差不多,但为什么要这样在前加一个修饰性的关键字呢,再比如:DELPHI中即使在声明时写了override;我们仍然可以把inherited拿掉,不执行父类方法.所以我认为JAVA与C++中的这种简洁性不错,当然这么多语言中我认为C/C++很不错,其次是JAVA,再是C#,DELPHI.
小弟见识尚短,请大家多多指教!
   
0 请登录后投票
时间:2006-06-22
LuckyFox 写道
赞一个,一直觉得自己对override和overload理解还是比较不错的,今天又发现有了covariance这个说法。呵呵,这个应该是为了描述override和overload之间比较混淆的方式而出现的吧?

C#里面约束的比较好,要么就override,要么就new,要么就overload。开始还认为这种做法太细了,有点小家子气,弄的我很是不爽。今天看了这篇文章,觉得这样还是有好处的。:)


父类本来没有foo,子类定义了foo,这是new,如果父类修改之后,增加了foo,那么子类不就要跟着由new改为override了?
   
0 请登录后投票
时间:2006-06-22
jkit 写道
LuckyFox 写道
赞一个,一直觉得自己对override和overload理解还是比较不错的,今天又发现有了covariance这个说法。呵呵,这个应该是为了描述override和overload之间比较混淆的方式而出现的吧?

C#里面约束的比较好,要么就override,要么就new,要么就overload。开始还认为这种做法太细了,有点小家子气,弄的我很是不爽。今天看了这篇文章,觉得这样还是有好处的。:)


父类本来没有foo,子类定义了foo,这是new,如果父类修改之后,增加了foo,那么子类不就要跟着由new改为override了?


我说的C#中的语法,你这个例子就正好说明了new的用法。父类没有foo,子类有foo,这个地方不用加new关键字,因为只有子类有foo,父类没有。
如果父类增加了方法foo而且签名和子类的foo一摸一样,这个时候子类的foo也并没有override调父类的foo方法,只是编译器回告诉你,你最好给你的子类foo方法加一个new的关键字,来说明,你的方法不是一个override的,虽然它并没有override调父类的foo。呵呵,感觉很绕口。

但是如果你需要override父类的foo,必须满足:
1,父类的foo是个virtaul的方法,并且子类加上了override关键字。
2,父类的foo是个abstract方法。
   
0 请登录后投票
时间:2006-06-23
我刚才试了一下,对“Java 语法不支持 method parameter covariance”有所疑问
我的源代码
[code:1]public class Parent {

private void func(Number n) {
System.out.println("Parent func(Number n)");
}

}public class Child extends Parent {

private void func(Number n) {
System.out.println("Child func(Number n)");
}

private void func(Integer i) {
System.out.println("Child func(Integer i)");
}

public static void main(String[] args) {
Number n = new Integer(0);
Integer i = new Integer(1);
Number nNull = null;
Integer iNull = null;

Child child = new Child();
child.func(n);
child.func(i);
child.func(nNull);
child.func(iNull);
child.func(null);
}
}[/code:1]
输出结果:
Child func(Number n)
Child func(Number n)
Child func(Number n)
Child func(Number n)
Child func(Number n)


应该用integer类型传入也是调用的Child func(Number n)呀,应该算是参数类型继承的例子了吧,是不是我理解的不对呀!
   
0 请登录后投票
时间:2006-06-23
首先,你这个仅仅存在overload,没有override。父类的方法是private。子类从何继承?

另外,你的输出结果是错误的,你确定你测试过你写的代码?
   
0 请登录后投票
时间:2006-06-23
fangjieke 写道
我刚才试了一下,对“Java 语法不支持 method parameter covariance”有所疑问
我的源代码
[code:1]public class Parent {

private void func(Number n) {
System.out.println("Parent func(Number n)");
}

}public class Child extends Parent {

private void func(Number n) {
System.out.println("Child func(Number n)");
}

private void func(Integer i) {
System.out.println("Child func(Integer i)");
}

public static void main(String[] args) {
Number n = new Integer(0);
Integer i = new Integer(1);
Number nNull = null;
Integer iNull = null;

Child child = new Child();
child.func(n);
child.func(i);
child.func(nNull);
child.func(iNull);
child.func(null);
}
}[/code:1]
输出结果:
Child func(Number n)
Child func(Number n)
Child func(Number n)
Child func(Number n)
Child func(Number n)


应该用integer类型传入也是调用的Child func(Number n)呀,应该算是参数类型继承的例子了吧,是不是我理解的不对呀!


在我的jdk1.4.2_08中输出跟你不同,我的是:

Child func(Number n)

Child func(Integer i)

Child func(Number n)

Child func(Integer i)

Child func(Integer i)
   
0 请登录后投票
时间:2006-06-23
不好意思,刚才把代码贴错了,应该是在child类中去掉func(integer i)方法,即代码如下

[code:1]public class Parent {

public void func(Number n) {
System.out.println("Parent func(Number n)");
}

}
public class Child extends Parent {

public void func(Integer i) {
System.out.println("Child func(Integer i)");
}

public static void main(String[] args) {
Number n = new Integer(0);
Integer i = new Integer(1);
Number nNull = null;
Integer iNull = null;

Child child = new Child();
child.func(n);
child.func(i);
child.func(nNull);
child.func(iNull);
child.func(null);
}
}[/code:1]

我们只能说它是override,不能说是overload,这点我明白了
可是为什么说它会产生ambiguous 呢?不明白!

LuckyFox 写道
首先,你这个仅仅存在overload,没有override。父类的方法是private。子类从何继承?

另外,你的输出结果是错误的,你确定你测试过你写的代码?
   
0 请登录后投票
论坛首页 Java版 OO

跳转论坛:
JavaEye推荐