java - this意思 - super;




為什麼this()和super()必須是構造函數中的第一條語句? (12)

所以,它不會阻止你在調用super之前執行邏輯。 它只是阻止你執行不適合單個表達式的邏輯。

實際上,你可以執行幾次執行的邏輯,你只需要將你的代碼封裝在一個靜態函數中,並在超級語句中調用它。

使用你的例子:

public class MySubClassC extends MyClass {
    public MySubClassC(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(createList(item));  // OK
    }

    private static List createList(item) {
        List list = new ArrayList();
        list.add(item);
        return list;
    }
}

Java要求如果在構造函數中調用this()或super(),它必須是第一條語句。 為什麼?

例如:

public class MyClass {
    public MyClass(int x) {}
}

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
    }
}

Sun編譯器說“調用super必須是構造函數中的第一條語句”。 Eclipse編譯器說“構造函數調用必須是構造函數中的第一條語句”。

但是,您可以通過稍微重新安排代碼來解決此問題:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        super(a + b);  // OK
    }
}

這是另一個例子:

public class MyClass {
    public MyClass(List list) {}
}

public class MySubClassA extends MyClass {
    public MySubClassA(Object item) {
        // Create a list that contains the item, and pass the list to super
        List list = new ArrayList();
        list.add(item);
        super(list);  // COMPILE ERROR
    }
}

public class MySubClassB extends MyClass {
    public MySubClassB(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(Arrays.asList(new Object[] { item }));  // OK
    }
}

所以,它不會阻止你在調用super之前執行邏輯 。 它只是阻止你執行不適合單個表達式的邏輯。

調用this()有類似的規則。 編譯器說“調用它必須是構造函數中的第一條語句”。

為什麼編譯器有這些限制? 你可以舉一個代碼示例,如果編譯器沒有這個限制,會發生什麼壞事?


構造函數按推導順序完成執行是有意義的。 因為超類沒有任何子類的知識,所以它需要執行的任何初始化與子類執行的任何初始化都是分開的,並且可能是先決條件。 因此,它必須首先完成它的執行。

一個簡單的演示:

class A {
    A() {
        System.out.println("Inside A's constructor.");
    }
}

class B extends A {
    B() {
        System.out.println("Inside B's constructor.");
    }
}

class C extends B {
    C() {
        System.out.println("Inside C's constructor.");
    }
}

class CallingCons {
    public static void main(String args[]) {
        C c = new C();
    }
}

這個程序的輸出是:

Inside A's constructor
Inside B's constructor
Inside C's constructor

Actually, super() is the first statement of a constructor because to make sure its superclass is fully-formed before the subclass being constructed. Even if you don't have super() in your first statement, the compiler will add it for you!


你問為什麼,以及其他答案,imo,並沒有真正說出為什麼可以調用你的超級構造函數,但只有它是第一行。 原因是你沒有真的調用構造函數。 在C ++中,等效的語法是

MySubClass: MyClass {

public:

 MySubClass(int a, int b): MyClass(a+b)
 {
 }

};

當你看到像這樣的初始化子句時,在開放大括號之前,你知道它是特殊的。 它在構造函數的其餘部分運行之前運行,實際上在任何成員變量初始化之前運行。 對於Java來說並沒有那麼不同。 在子類的任何成員被初始化之前,有一種方法可以在構造函數真正啟動之前運行一些代碼(其他構造函數)。 那樣就是把“呼叫”(例如super )放在第一線。 (從某種意義上說, super或者在第一個開放大括號之前是這樣的,即使你之後輸入了它,因為它會在你完全構造完成之前執行)。開放大括號之後的任何其他代碼(比如int c = a + b; )讓編譯器說:“哦,好吧,沒有其他構造函數,我們可以初始化所有東西。” 所以它會跑掉並初始化你的超類和你的成員,然後在開啟大括號後開始執行代碼。

如果幾行後,它會遇到一些代碼,說:“當你構建這個對象時,是的,這裡是我希望你傳遞給基類的構造函數的參數”,它已經太晚了,也沒有有道理。 所以你得到一個編譯器錯誤。


因為JLS這樣說。 JLS可以以兼容的方式更改以允許它嗎? 對。 但是,這會使語言規範複雜化,這已經足夠複雜了。 這不是一件非常有用的事情,並且有辦法繞過它(用另一個構造函數調用方法this(fn()) - 該方法在另一個構造函數之前調用,因此也是超級構造函數) 。 所以做這個改變的權重比是不利的。

編輯20183月:消息記錄:構建和驗證 Oracle建議刪除此限制(但與C#不同,在構造器鏈接之前, this明確未分配 (DU))。

歷史上,this()或super()必須在構造函數中第一個。 這種限制從來不受歡迎,並且被視為任意。 有許多微妙的原因,包括驗證invokespecial,導致了這個限制。 多年來,我們已經在虛擬機級別解決了這些問題,到了考慮解除這一限制變得切實可行的時候,不僅僅是為了記錄,而是為了所有構造函數。


在構建子對象之前,必須創建父對象。 正如你知道你寫這樣的課時:

public MyClass {
        public MyClass(String someArg) {
                System.out.println(someArg);
        }
}

它轉向下一個(擴展和超級只是隱藏):

public MyClass extends Object{
        public MyClass(String someArg) {
                super();
                System.out.println(someArg);
        }
}

首先我們創建一個Object ,然後將這個對象擴展到MyClass 。 我們無法在Object之前創建MyClass 。 簡單的規則是必須在子構造函數之前調用父項的構造函數。 但我們知道類可以有更多的構造函數。 Java允許我們選擇一個將被調用的構造函數(它將是super()super(yourArgs...) )。 所以,當你寫super(yourArgs...)你重新定義了將被調用來創建一個父對象的構造函數。 你不能在super()之前執行其他方法,因為對像還不存在(但是在super()一個對象將被創建,你將能夠做任何你想做的事)。

那麼為什麼我們不能在任何方法之後執行this() ? 如你所知, this()是當前類的構造函數。 我們也可以在我們的類中使用不同數量的構造函數,並將它們稱為this()this(yourArgs...) 。 正如我所說的每個構造函數都有隱藏方法super() 。 當我們編寫我們的自定義super(yourArgs...)我們使用super(yourArgs...)移除super() super(yourArgs...) 。 當我們定義this()this(yourArgs...)我們也在當前構造函數中移除我們的super() ,因為如果super() this()在同一個方法中使用this() ,它會創建多個父對象。 這就是為什麼對this()方法施加相同的規則。 It just retransmits parent object creation to another child constructor and that constructor calls super() constructor for parent creation. So, the code will be like this in fact:

public MyClass extends Object{
        public MyClass(int a) {
                super();
                System.out.println(a);
        }
        public MyClass(int a, int b) {
                this(a);
                System.out.println(b);
        }
}

As others say you can execute code like this:

this(a+b);

also you can execute code like this:

public MyClass(int a, SomeObject someObject) {
    this(someObject.add(a+5));
}

But you can't execute code like this because your method doesn't exists yet:

public MyClass extends Object{
    public MyClass(int a) {

    }
    public MyClass(int a, int b) {
        this(add(a, b));
    }
    public int add(int a, int b){
        return a+b;
    }
}

Also you are obliged to have super() constructor in your chain of this() methods. You can't have an object creation like this:

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}

我完全同意,限制太強。 使用靜態輔助方法(如Tom Hawtin - tackline建議)或將所有“pre-super()計算”推送到參數中的單個表達式中並非總是可行,例如:

class Sup {
    public Sup(final int x_) { 
        //cheap constructor 
    }
    public Sup(final Sup sup_) { 
        //expensive copy constructor 
    }
}

class Sub extends Sup {
    private int x;
    public Sub(final Sub aSub) {
        /* for aSub with aSub.x == 0, 
         * the expensive copy constructor is unnecessary:
         */

         /* if (aSub.x == 0) { 
          *    super(0);
          * } else {
          *    super(aSub);
          * } 
          * above gives error since if-construct before super() is not allowed.
          */

        /* super((aSub.x == 0) ? 0 : aSub); 
         * above gives error since the ?-operator's type is Object
         */

        super(aSub); // much slower :(  

        // further initialization of aSub
    }
}

正如Carson Myers建議的那樣,使用“對象未構造”異常會有所幫助,但在每個對象構造過程中檢查這個異常會減慢執行速度。 我希望Java編譯器能夠做出更好的區分(而不是通過禁止if語句,但允許參數中的?-operator),即使這會使語言規範複雜化。


我發現了一個動物。

這不會編譯:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
        doSomething(c);
        doSomething2(a);
        doSomething3(b);
    }
}

這工作:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        this(a + b);
        doSomething2(a);
        doSomething3(b);
    }

    private MySubClass(int c) {
        super(c);
        doSomething(c);
    }
}

我相當肯定(那些熟悉Java規範的人),它是為了防止你被允許使用部分構造的對象,並且(b)強制父類的構造函數構造一個“新鮮的“對象。

一些“壞”事情的例子是:

class Thing
{
    final int x;
    Thing(int x) { this.x = x; }
}

class Bad1 extends Thing
{
    final int z;
    Bad1(int x, int y)
    {
        this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
        super(x);
    }        
}

class Bad2 extends Thing
{
    final int y;
    Bad2(int x, int y)
    {
        this.x = 33;
        this.y = y; 
        super(x); // WHOOPS! x is supposed to be final
    }        
}

我知道我晚了一點,但我已經使用了這個技巧幾次(我知道這有點不尋常):

我使用一種方法創建通用接口InfoRunnable<T>

public T run(Object... args);

如果我在將它傳遞給構造函數之前需要做一些事情,我只需要這樣做:

super(new InfoRunnable<ThingToPass>() {
    public ThingToPass run(Object... args) {
        /* do your things here */
    }
}.run(/* args here */));

父類的constructor需要在子類的constructor之前調用。 這將確保如果您在構造函數的父類中調用任何方法,則父類已正確設置。

你所要做的,將參數傳遞給超級構造函數是完全合法的,你只需要像你在做的那樣內聯地構造這些參數,或者將它們傳遞給你的構造函數,然後將它們傳遞給super

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

如果編譯器沒有執行這個,你可以這樣做:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

parent類具有默認構造函數的情況下, compiler會自動為您插入對super的調用。 由於Java中的每個類都從Object繼承,因此必須以某種方式調用對象構造函數,並且必須先執行它。 編譯器自動插入super()允許這樣做。 強制超級先出現,強制執行構造函數體的順序是:Object - > Parent - > Child - > ChildOfChild - > SoOnSoForth


class C
{
    int y,z;

    C()
    {
        y=10;
    }

    C(int x)
    {
        C();
        z=x+y;
        System.out.println(z);
    }
}

class A
{
    public static void main(String a[])
    {
        new C(10);
    }
}

See the example if we are calling the constructor C(int x) then value of z is depend on y if we do not call C() in the first line then it will be the problem for z. z would not be able to get correct value.





constructor