[JAVA]重写父类方法并向上转型时的初始化问题

来源:互联网 发布:js定时器重复执行 编辑:程序博客网 时间:2024/06/05 18:10

其实是个小问题,不过蛮有意思的。

这个问题的提出,是今天朋友问我的一个问题。我来大概描述一下这个问题:

如何模仿安卓的Activity,设计一个父类,使得对象被创建(调用)时某些周期函数依次被调用,且该性质不因子类的重写而被破坏。举个例子:

open class sup(){    //假设父类已实现A -> B -> C 的周期执行顺序    open fun A(){        println("f A")    }    open fun B(){        println("f B")    }    open fun c(){        println("f C")    }}class sub:sup(){    override fun A() {        super.A()        println("这行执行完再执行B()")    }    override fun B() {        println("c B")    }}

应当预计输出:

f A这行执行完再执行B()c Bf C

其实这就是安卓Activity的几个生命周期,都很熟悉了:


我说很简单啊,在父类的构造或初始化函数中写个顺序就好了:

init {    println("父类构造")    A()    B()    C()}

经过测试,这样写是没问题的,输出符合上面的预期。


然后他问了一句话,困扰了我不少时间(现在想来应该还是我对继承和重写的理解没有到位导致的问题):

他说,就算是向上转型,但这时候父类初始函数中写的A、B、C,为什么会是被重写过的子类中的A、B、C方法,子类不是还没构造么?如果子类A方法中要输出子类私有变量var1的值,但此时子类还没构造(假设子类var1在子类构造函数中赋值),岂不是直接就空了

我们都知道,构造时的执行顺序是:父类静态 -> 子类静态 -> 父类一般语句 -> 父类构造 -> 子类一般语句 -> 子类构造

他说的这个问题,好像有点道理啊...


可能文字说明比较不好理解,那我们转换成代码,结合红字标示的顺序来看一下这个问题:

public class Test {    public static void main(String[] arg){        sup1 s1 = new sub1();//会输出什么呢?    }}class sup1{    public sup1(){        System.out.println("父类");        A();        B();        C();    }    public void A(){        System.out.println("f A");    }    public void B(){        System.out.println("f B");    }    public void C(){        System.out.println("f C");    }}class sub1 extends sup1{    public int var1 = 8;    public sub1(){        System.out.println("子类");        var1 = 7 ;    }    public void A(){        super.A();        System.out.println(var1);    }}

所以我就开始想,难不成父类中构造方法,执行的是父类中的ABC,因为这样可以避免被子类重写后,抛出Null异常。毕竟上层如果不对下层开放,这么去处理,真的是个非常大的隐患:下层根本查不出来(先不考虑这么设计父类合理不合理)于是我想,Java应该会有避免机制的吧.....应该有的吧....


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

然而,上面这段代码最后输出:

父类f A0 // 不是8 也不是7 ; 同时也说明了执行了子类中重写的方法。0是默认赋值,但如果var1是个对象,这里就真的是空的。f Bf C子类
执行顺序没问题,但是值var1确实空了。居然真的空了Orz......害怕.jpg

感觉不想点别的方法, 这会是个隐蔽性很高的雷...我拿据称Null控制机制做得很好的Kotlin去试了一下,在非"?"的情况下编译通过,但是也是抛空。也就是说,这是一个现有机制检测不出的Null隐患...
先马克一下,硬脆思婷。



PS:

我去看了安卓的源码,大概知道了他的处理方式。简单说一下,他不是放在构造函数里完成对象创建时自动按周期函数执行,而是通过一个ActivityMannager来处理。ActivityMannager管理一个任务栈,任务栈调用栈中某个Activity中的非构造函数callOnxxx()。这个函数类似于上面的init{},不太一样,但是本质上是一样的。

大概就像这样:

class MyActivity{    public void onCreate(){        System.out.println("F onCreate");    }    public void onStart(){        System.out.println("F onStart");    }    public void onResume(){        System.out.println("F onResume");    }    public void onPause(){        System.out.println("F onPause");    }    public void onStop(){        System.out.println("F onStop");    }    public void onDestory(){        System.out.println("F onDestory");    }    public void onRestart(){        System.out.println("F onRestart");    }    //任务栈调用唤醒周期函数callOnXX()    public final void callOnCreate(){        onCreate();        callOnStart();    }    public final void callOnStart(){        onStart();        onResume();    }    public final void callOnPause(){        onPause();        onStop();    }    public final void callOnRestart(){        onRestart();        callOnStart();    }    public final void callOnResume(){        onResume();    }}//测试类class TestActivity extends MyActivity{    @Override    public void onStart() {        super.onStart();        System.out.println("C onStart");    }}


阅读全文
0 0
原创粉丝点击