其实是个小问题,不过蛮有意思的。
这个问题的提出,是今天朋友问我的一个问题。我来大概描述一下这个问题:
如何模仿安卓的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 B
f 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 A
0 // 不是8 也不是7 ; 同时也说明了执行了子类中重写的方法。0是默认赋值,但如果var1是个对象,这里就真的是空的。
f B
f 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");
}
}