java構造方法不允許調用重載方法

java構造方法不允許調用重載方法     

 

    今年在搭建一個項目的基礎架構的時候,遇到了一個問題,一個類的初始化對象總是得到錯誤的數據,從常規語法上不到賦值錯誤。java代碼大概如下:

import org.junit.Test;

public class ConstructorInvokeOverrideTest {
    @Test
    public void test() {
        new FlowView(1234);
    }

    class FlowView extends BaseView {
        int layoutId;

        public FlowView(/*Context context, */ int layoutId) {
            super(/*context*/);
            this.layoutId = layoutId;
        }

        @Override
        protected int getLayoutId() {
            return layoutId;
        }
    }

    abstract class BaseView {

        BaseView(/*Context context*/) {
            inflate(getLayoutId());
        }

        public void inflate(int layoutId) {
            System.out.println("layoutId: " + layoutId);
        }

        protected abstract int getLayoutId();
    }
}

輸出結果

layoutId: 0

 

按照設計思路,此處應該是打印layoutId:1234。可是事與願違,打印的是layoutId。因爲實際代碼行數很多,當時模塊比較多,一直以爲是其他模塊的初始化導致的,然後叫坐在身邊的同事一起review代碼,對方也沒看出個所以然(這裏告訴大家一個查bug的技巧,在實戰中,交叉review是非常高效的解決問題方法,可以排除一些常規的低級錯誤,與開發人員雙方水平無關)

 

範例說明:

1. 在實際代碼中,一直是拋出異常;

2. 爲了避嫌避免泄漏商業代碼,構造函數並非是layoutId,重載的函數也不是getLayoutId();

3. Context是在Android中的一個上下文句柄,此處代碼爲了簡潔,我們忽略掉該初始化參數,加上註釋的原因是爲了讓讀者更好理解實際繼承時是有初始化參數的。

 

問題原因:

對於這個問題,只能推翻重來,把本次修改的內容一點點回退。最終發現原來犯了一個語法錯誤。

1. FlowView中定義了成員變量layoutId;

2. BaseView的getLayoutId是一個可以重載的方法;

3. FlowView繼承並覆蓋了getLayoutId並且BaseView的構造函數中調用了getLayoutId;

那麼就導致一個問題,父類在構造函數調用繼承重載方法時,子類並沒有完成實例化(因爲父類和子類的構造函數還沒有執行完),子類FlowView成員變量layoutId可能還沒有完成初始化。

 

發現這個問題後,一百度,果然很多人犯過在構造方法中調用重載方法的錯誤。修改這個問題也很簡單,在構造函數中不調用重載方法。

 

另外,BaseView的getLayoutId是一個抽象方法。即使不用抽象方法,結果還是一樣。

public class ConstructorInvokeOverrideTest {
    @Test
    public void test() {
        new FlowView(1234);
    }

    class FlowView extends BaseView {
        int layoutId;

        public FlowView(/*Context context, */ int layoutId) {
            super(/*context*/);
            this.layoutId = layoutId;
        }

        @Override
        protected int getLayoutId() {
            return layoutId;
        }
    }

    // no abstract class.
    class BaseView {

        BaseView(/*Context context*/) {
            inflate(getLayoutId());
        }

        public void inflate(int layoutId) {
            System.out.println("layoutId: " + layoutId);
        }

        protected int getLayoutId() {
            return -1;
        }
    }
}

輸出結果:

layoutId: 0

 

注意:是0,而不是-1。 因爲getLayoutId()已經被重載覆蓋了。

 

最後,輸出結果可能在Oracle JDK18和Android上面的結果可能不一樣。

本博客的運行環境是:

java version "1.8.0_152"

Java(TM) SE Runtime Environment (build 1.8.0_152-b16)

Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章