当前位置: 代码迷 >> 综合 >> override, overload, covariance
  详细解决方案

override, overload, covariance

热度:94   发布时间:2024-01-15 13:19:51.0
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.
  相关解决方案