簡體   English   中英

為什么在訪問字段之前必須調用超級構造函數?

[英]Why does the super constructor have to be called before fields can be accessed?

我的一個類繼承自我使用的框架中的類。 超類在其構造函數中調用一個方法,我在自己的類中覆蓋它。 該方法使用我想要在超級構造函數調用之前初始化的字段以避免NullPointerException。

有沒有辦法做到這一點?

這是一個綜合測試場景,我希望在call時, Child c不為null。

public class Test {

    public static class Parent {
        public Parent() {
            super();
            call();
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Parent");
        }
    }

    public static class Child extends Parent {

        private Child c = this;

        public Child() {
            super();
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Child, c is " + (c == null ? "null" : "this"));
        }
    }

    public static void main(String[] args) {
        new Child();
    }
}

在Java 7之前,這是可能的。 我可以得到這樣的特技:

    public static class Child extends Parent {

        private Child c;

        private Child(Object unused) {
            super();
        }

        public Child() {
            this(c = this);
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Child, c is " + (c == null ? "null" : "this"));
        }
    }

現在,這將不再適用。 我很欣賞額外的安全性,但是超級電話會破壞它所帶來的任何安全性並降低靈活性。

我想要一種規避這種限制的方法。 作為替代方案,我想知道什么是通過限制來獲得超級構造函數的情況。

將在超類構造函數之前調用靜態初始值設定項。 但是,您將無法設置任何非靜態字段,因此很可能無濟於事。

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

非超級構造函數完成后調用非靜態初始化塊也沒有幫助。

另一種方法可能是在從超級構造函數調用時不執行任何操作,並再次調用子構造函數,例如:

    public Child() {
        super();
        call();
    }

    public void call() {

       if (c==null) {
         return;
       }

       System.out.println("do something with c now");

    }

如果在依賴於此方法的超級構造函數中發生更多內容,這將不起作用。

我不得不同意EJP的觀點,這是一個壞主意; 找到一個完全不同的解決方案,不涉及折磨構造函數會好得多。

請注意,Java編譯器將您的類Child轉換為以下等效項:

public static class Child extends Parent {

    private Child c;

    public Child() {
        super();
        c = this;
    }

    // Remaining implementation
}

對於Java 6和7,這是相同的,當使用任何兩個版本進行編譯時,構造函數的生成字節代碼甚至是相同的。 在調用超級構造函數之后,始終會實例化本地字段。 您使用什么編譯器來使您的“解決方案”工作?

這種限制是非常基本的。 這樣,您可以依賴首先應用的超級構造函數。 想象一下,你的子構造函數正在使用在這個類中聲明的final字段。 如果您不保證此構造函數執行順序,則無法保證此字段已初始化。 這種限制使Java更可靠。

這是對“我想知道通過超級構造函數案例的限制所獲得的內容”的答案。 部分問題。

在構造過程中,有三種狀態,在類X中聲明的字段可能在:所有默認值,全部初始化為一致的工作值,以及其他任何內容。

目標似乎是除了X之外的類中的代碼只能看到前兩個狀態中的一個。 當任何X超類的非靜態初始化程序或構造函數代碼正在運行時,X的字段都處於默認狀態。 當X的任何子類的非靜態初始化程序或構造函數代碼正在運行時,所有X的字段都已初始化為完全一致的可用狀態。

只有X初始化程序和構造函數代碼必須處理處於不一致狀態的X字段,一些是初始化的,一些是默認的,一些是部分初始化的。

通過從X超類初始化器或構造函數調用X方法可以避免這種情況,但這通常被視為反模式。 問題是運行的X代碼不是從部分構造的X中的初始化程序或構造函數本地調用的。如果該代碼更改字段,則在X初始化程序和構造函數體運行時可能會覆蓋此更改。

這應該永遠不會起作用。

請注意,在字節碼級別,實際上允許這樣做。 在字節碼中,您可以在調用超類構造函數之前設置當前類中聲明的字段。 但是,Java無法使用此行為。 它僅由Java編譯器秘密使用,以初始化為支持內部類而添加的合成字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM